TMUX die Screen Alternative

TMUX ist eine nette Alternative zu Screen, es unterstützt per Default das Teilen eines Fensters horizontal oder/und vertikal.

Installation von TMUX

#Für Ubuntu / Debian:
apt install tmux

#Für Fedora / RedHat / CentOS:
yum install tmux

Tastenkürzel (Hotkeys)

HotkeyBedeutung
CTRL-B ?Anzeiger aller Hotkeys
CTRL-B D(detatch) Trennen der aktuellen Sitzung
CTRL-B S(switch) Wechseln zwischen verschiedenen Sitzungen
CTRL-B C(create) Neues Fenster
CTRL-B &Beenden des Fensters
CTRL-B ,Umbennennen des Fensters
CTRL-B "Fenster horizonal Teilen
CTRL-B %Fenster vertikal Teilen
CTRL-B PfeiltastenWechseln zwischen den Panes (Fensterscheiben)
CTRL-B WListe aller Fenster anzeigen
CTRL-B [SPACE]Pane Größe ändern
CTRL-B L(last) Wechseln zum letzen Fenster, ist mit einem Minus markiert
CTRL-B Pvorheriges Fenster
CTRL-B Nnächstes Fenster
CTRL-B 1...Wechsel zu Fenster Nr.
CTRL-B !Pane Lösen und neues Fenster

per SSH direkt in tmux Sitzung starten

Praktisches Feature wenn man seine Sitzung nicht verlieren möchte bei einem Verbindungsabriss. Dazu muss die .bashrc des Users angepasst werden.

Einfach am Ende folgendes einfügen:

if [[ -z "$TMUX" ]] && [ "$SSH_CONNECTION" != "" ]; then
    tmux attach-session -t ssh_tmux || tmux new-session -s ssh_tmux
fi

tmux Scripting

Man kann auch ganz Easy sich eigene Umgebungen scripten.

Hier ein angepasstes Beispiel von ubuntuusers.de (https://wiki.ubuntuusers.de/tmux/):

#!/bin/bash
SESSION=main
tmux="tmux -2"

# if the session is already running, just attach to it.
$tmux has-session -t $SESSION
if [ $? -eq 0 ]; then
       echo "Session $SESSION already exists. Attaching."
       sleep 1
       $tmux attach -t $SESSION
       exit 0;
fi
                                 
# create a new session, named $SESSION, and detach from it
$tmux new-session -d -s $SESSION
$tmux new-window    -t $SESSION:0 
$tmux rename-window    -t $SESSION:0 "Default SHELL"
$tmux split-window  -h -t $SESSION:0
$tmux new-window    -t $SESSION:1 "tail -f /var/log/syslog"
$tmux rename-window    -t $SESSION:1 "SYSLOG"
$tmux new-window    -t $SESSION:2 "top"
$tmux rename-window    -t $SESSION:2 "TOP"
$tmux new-window    -t $SESSION:3 "tail -f /var/log/mail.log"
$tmux rename-window    -t $SESSION:3 "MAILLOG"
$tmux split-window  -h -t $SESSION:3
$tmux new-window    -t $SESSION:4 "tail -f /omd/sites/dev1/var/log/cmc.log"
$tmux rename-window    -t $SESSION:4 "CMCLOG"
$tmux select-window -t $SESSION:0
$tmux attach -t $SESSION

 

 

Checkpoint: ClusterXL critical required interfaces in bond (LACP) configuration

Wenn man auf Checkpoint ClusterXL Gateways Bondings baut muss man aufpassen das man den „critical required interfaces“ Parameter unter Umständen anpassen muss, besonders wenn man mehr als 2 Interfaces hat.

Das Standardverhalten von Checkpoint ist das bei n Interfaces bei n-1 ClusterXL davon ausgeht das der Link tot ist und ein HA Failover eingeleitet wird.

Dieses Verhalten ist eventuell nicht gewünscht wenn man z.B. 4x 10 GB Links hat und die Umgebung problemlos auch mit nur 2 Links läuft.

Um dies zu Ändern muss die Datei $FWDIR/conf/cpha_bond_ls_config.conf angepasst werden.

Jede Zeile sollte folgenden Syntax haben:

<bondname> <critical#>

Beispiel:

# File name: $FWDIR/conf/cpha_bond_ls_config.conf
# This file is used to describe for each bond in Active/Active mode
# what is the required number of active slaves.
# If the number of active slaves on a bond is lower then the required
# slaves number, the bond interface will be marked as DOWN.
# If there is a bond interface in Active/Active, and there is no
# entry for it in this file, its required slaves num will be:
# The number of configured slaves minus 1.
#
# Fill this file in the following syntax:
#  
# Example:
# bond0 2
bond0 2
bond1 2

d.H. es müssen 2 Interfaces noch im Bond sein bevor ClusterXL annimmt das der Link tot ist.

Dokumentation dazu:

ClusterXL R80.10 (Part of Check Point Infinity) Administration Guide (nach „Setting Critical Required Interfaces“ suchen)

ClusterXL R77.30 Administration Guide (nach „Setting Critical Required Interfaces“ suchen)

 

 

Check_MK: Inventory erweitern mit SNMP Daten

Check_MK hat seit Version 1.2.5i1 das Feature Inventory an Board. Per Default werden von Systemen CPU, Memory, Harddisks, Softwarepakete und vieles mehr eingesammelt. Die Anleitung dient dazu das Inventory zu erweitern und eigene Informationen per SNMP einzusammeln und per View durchsuchbar zu machen.

Ich habe mittlerweile mehrere eigene Ergänzungen gemacht, darunter CDP Neighbors, Dot1X Authentications, Wireless Accesspoints und Hardware Inventory von Cisco Geräten.

Als Beispiel zeige ich das Cisco Hardware Inventory mit Seriennummern.

Bestandteile für eine Inventory Erweiterung:

  1. Daten sammeln -> Erweiterung für den Inventory Collector unter  ~/local/share/check_mk/inventory/
  2. Daten darstellen -> Erweiterung für die Inventory Views unter ~/local/share/check_mk/web/plugins/views/

1. Daten sammeln

Die Inventory Daten werden innerhalb einer Baumstruktur abgelegt. Man kann für seine eigenen Daten neue Knoten erzeugen. z.B. unter networking das hw_inventory wird im Tree dargestellt als networking.hw_inventory: . Der Doppelpunkt zeigt an das Daten unterhalb des Knotens folgen.

Das Inventory Plugin besteht aus einer Funktion und dem inv_info Directory welches alle Inventory Plugins kennt. In inv_info  im Key snmp_scan_function ist genauso wie bei den Check_MK SNMP Checks angegeben ob Check_MK überhaupt Daten finden wird bei einem SNMP Bulkwalk. snmp_info beinhaltet die OID und alle Untertabellen mit Daten die von Check_MK eingesammelt werden sollen.

Beispiel: ~/local/share/check_mk/inventory/my_hw_inventory.py

#!/usr/bin/python

port = {
0: "-",
1: "other",
2: "unknown",
3: "chassis",
4: "backplane",
5: "container",
6: "powerSupply",
7: "fan",
8: "sensor",
9: "module",
10: "port",
11: "stack",
12: "cpu"

}

def inv_hw_inventory(info, params):
    
    node = inv_tree("networking.hw_inventory:")

    for x in info[0]:
        
        #print x
        
        # If Serial Number is longer than 1 char
        if len(x[10]) >= 1:
        
            fru = "true" if "1" in x[15] else "false"
            
            classx = port[int(x[4])] if len(x[4]) is 1 else port[0]
            
            node.append(
            {
            "index": x[0],
            "description": x[1],
            "vendor_type": x[2],
            "contained_in_id": x[3],
            "class": classx,
            "parent_rel_pos": x[5],
            "name": x[6],
            "hw_rev": x[7],
            "fw_rev": x[8],
            "sw_rev": x[9],
            "serial_number": x[10],
            "manufactor": x[11],
            "model": x[12],
            "alias": x[13],
            "asset_id": x[14],
            "is_fru": fru,
            }
            )
        

inv_info["inv_hw_inventory"] = {
"inv_function": inv_hw_inventory,
"snmp_info": [(".1.3.6.1.2.1.47.1.1.1.1",[OID_END, # entPhysicalIndex
                                        "2", # entPhysicalDescr
                                        "3", # entPhysicalVendorType
                                        "4", # entPhysicalContainedIn
                                        "5", # entPhysicalClass
                                        "6", # entPhysicalParentRelPos
                                        "7", # entPhysicalName
                                        "8", # entPhysicalHardwareRev
                                        "9", # entPhysicalFirmwareRev
                                        "10", # entPhysicalSoftwareRev
                                        "11", # entPhysicalSerialNum                                        
                                        "12", # entPhysicalMfgName
                                        "13", # entPhysicalModelName
                                        "14", # entPhysicalAlias
                                        "15", # entPhysicalAssetID
                                        "16", # entPhysicalIsFRU
                                             ]),
             ],
"snmp_scan_function": lambda oid: oid(".1.3.6.1.2.1.1.2.0").startswith(".1.3.6.1.4.1.9.1"),
}

Mit der inv_tree Funktion wird ein neuer Inventory Baum eröffnet (siehe Code). Alle eingesammelten Informationen werden dann an diesen neuen Baum mit append als Dictionary hinzugefügt.

Die eigene Inventory Funktion kann man testen mit:

OMD[dev1]:~/local/share/check_mk/web/plugins/views$ cmk -v -i testswitch
Doing HW/SW-Inventory for testswitch...inv_hw_inventory snmp_extended_info snmp_os snmp_info ..unchanged..1603 entries..OK

Wenn in der Inventory Funktion ein pprint.pprint(info) eingebaut wird können auch die Rohdaten betrachtet werden.

2. Daten darstellen

Hierfür muss ein View gebaut werden der die Informationen des neuen Inventory Baums darstellt.

Beispiel ~/local/share/check_mk/web/plugins/views/my_inv_hw_inventory.py

#!/usr/bin/python

inventory_displayhints.update({

        ".networking.hw_inventory:"                          : { "title" : _("HW Inventory"), "render" : render_inv_dicttable,  "view" : "invhwi_of_host", "keyorder": 
                                                               ["index","name","description","model","contained_in_id","serial_number","hw_rev","fw_rev","sw_rev","class","manufactor","asset_id","alias","is_fru"] },
        ".networking.hw_inventory:*.index"                   : { "title" : _("Index") },
        ".networking.hw_inventory:*.description"             : { "title" : _("Description") },
        ".networking.hw_inventory:*.contained_in_id"         : { "title" : _("Contained in Index") },
        ".networking.hw_inventory:*.class"                   : { "title" : _("Class") },
        ".networking.hw_inventory:*.name"                    : { "title" : _("Name") },
        ".networking.hw_inventory:*.hw_rev"                  : { "title" : _("HW REV") },
        ".networking.hw_inventory:*.fw_rev"                  : { "title" : _("FW REV") },
        ".networking.hw_inventory:*.sw_rev"                  : { "title" : _("SW REV") },
        ".networking.hw_inventory:*.serial_number"           : { "title" : _("Serial Number") },
        ".networking.hw_inventory:*.manufactor"              : { "title" : _("Manufactor") },
        ".networking.hw_inventory:*.model"                   : { "title" : _("Model") },
        ".networking.hw_inventory:*.alias"                   : { "title" : _("Alias") },
        ".networking.hw_inventory:*.asset_id"                : { "title" : _("Asset ID") },
        ".networking.hw_inventory:*.is_fru"                  : { "title" : _("Is FRU") },
})

declare_invtable_view("invhwi", ".networking.hw_inventory:", _("HW Inventory"), _("HW Inventory"))

Danach sollte man mit „omd restart apache && cmk -v -R“ Check_MK und den Apachen neu starten, damit die neuen Views sichtbar werden. Damit Daten angezeigt werden muss 1 mal das Inventory auf dem Gerät gelaufen sein.

Im Views Menü sollte dann unser neuer View unter Inventory auftauchen:

Man kann sich alle Daten anzeigen lassen oder gezielt nach einzelnen Informationen oder Geräten suchen.

Wenn man im Host View ist kann man über das obere Menü auch direkt zu den Inventory Daten kommen, wurde der Button noch nicht genutzt kann er sichtbar gemacht werden mit dem „…“ Button.

Die Daten werden für den Host als Baumstruktur dargestellt.

Viel Spaß beim Erweitern 😉

Python: Oracle DB Modul für Python für CentOS6

Quelle: https://gist.github.com/hangtwenty/5547377

#!/bin/bash

# INSTALL ORACLE INSTANT CLIENT #
#################################

# NOTE: Oracle requires at least 1176 MB of swap (or something around there).
# If you are using CentOS in a VMWare VM, there's a good chance that you don't have enough by default.
# If this describes you and you need to add more swap, see the
# "Adding a Swap File to a CentOS System" section, here:
# http://www.techotopia.com/index.php/Adding_and_Managing_CentOS_Swap_Space

# Install basic dependencies
sudo yum -y install libaio bc flex

echo "Now go get some the following two RPMs ..."
echo "- basic: oracle-instantclient11.2-basic-11.2.0.3.0-1.x86_64.rpm"
echo "- SDK/devel: oracle-instantclient11.2-devel-11.2.0.3.0-1.x86_64.rpm"
echo "... from this URL: http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html"
echo "WARNING: It's pretty annoying, they make you sign up for an Oracle account, etc."
echo 'I will assume you have put these two files are into ~/Downloads'
echo "Press any key once you're ready" && read -n 1 -s

sudo rpm -ivh ~/Downloads/oracle-instantclient11.2-basic-*
sudo rpm -ivh ~/Downloads/oracle-instantclient11.2-devel-*

# SET ENVIRONMENT VARIABLES #
#############################

# Source for this section: http://cx-oracle.sourceforge.net/BUILD.txt

# (SIDENOTE: I had to alter it by doing some digging around for where the Oracle RPMs really installed to;
# if you ever need to do this, do a command like this:
#     rpm -qlp <rpm_file_of_concern.rpm>)

echo '# Convoluted undocumented Oracle bullshit.' >> $HOME/.bashrc
echo 'export ORACLE_VERSION="11.2"' >> $HOME/.bashrc
echo 'export ORACLE_HOME="/usr/lib/oracle/$ORACLE_VERSION/client64/"' >> $HOME/.bashrc
echo 'export PATH=$PATH:"$ORACLE_HOME/bin"' >> $HOME/.bashrc
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$ORACLE_HOME/lib"' >> $HOME/.bashrc
. $HOME/.bashrc

# INSTALL cx_Oracle #
#####################

pip install cx_Oracle

Gute Anleitung zur Verwendung von cs_Oracle: http://www.oracle.com/technetwork/articles/dsl/prez-python-queries-101587.html

Beispiel:

import cx_Oracle

# db helper named arrays
def rows_to_dict_list(cursor):
    columns = [i[0] for i in cursor.description]
    return [dict(zip(columns, row)) for row in cursor]

# Connect to DB
dsn_tns = cx_Oracle.makedsn("10.10.10.1",1521,"TESTDB")
db = cx_Oracle.connect("testuser","password",dsn_tns)
cursor = db.cursor()

# Get data from DB
cursor.execute("SELECT * FROM test_tab")
result = rows_to_dict_list(cursor)

# Insert to DB
cursor.execute('INSERT INTO test_tab (row1, row2, row2) VALUES ("xxx", "yyy", "zzz")')
db.commit()

# close db
db.close()

 

Check_MK: Werte simulieren in einem SNMPwalk

Wer in Check_MK mittels gespeicherter SNMP Walks Checks entwickelt möchte unter umständen auch gerne sich ändernde Werte haben und keine statischen aus dem Dump.

Check_MK hat 3 eingebaute Funktionen um steigende Counter etc. zu simulieren.

Ausschnitt aus dem Check_MK Code lib/python/cmk_base/agent_simulator.py (GPLv2):

def agentsim_uptime(rate = 1.0, period = None): # period = sinus wave
    if period == None:
        return int(our_uptime() * rate)
    else:
        a = (rate * period) / (2.0 * math.pi)
        u = our_uptime()
        return int(u * rate + int(a * math.sin(u * 2.0 * math.pi / period)))


def agentsim_enum(values, period = 1): # period is in seconds
    hit = int(our_uptime()) / period % len(values)
    return values[hit]


def agentsim_sinus(base = 50, amplitude = 50, period = 300):
    return int(math.sin(our_uptime() * 2.0 * math.pi / period) * amplitude + base)



Wie kann man die Simulatoren verwenden?

Im gespeicherten SNMP Walk müssen die Simulatoren eingefügt werden statt des statisch exportieren Werts. Die SNMP Walks liegen unter ~/var/check_mk/snmpwalks/<hostname>

Beispiel:

.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.1.3 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.1 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.2 %{uptime(rate=10)}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.2.3 %{sinus()}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.1 %{enum([10,20,30,40,50,60,70,80,90])}
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.2 0
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.3.3 31008
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.4.1 5353
.1.3.6.1.4.1.9.9.189.1.3.5.1.4.10101.4.2 0

 

 

Python: Snippet Argumente für Command Line Tools mit getopt oder argparse

Mein persönlicher Favorit ist argparse, der Vollständigkeit halber aber beide Lösungen. getopt und argpase sind beide bei Python dabei und müssen nicht nachinstalliert werden.

GETOPT Ansatz:

#!/usr/bin/env python

import getopt
import sys

def usage():
    print "test1.py - A test script.\n" \
          " -p, --print Return a string \n" \
          " -h, --help Help"


def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "p:h", ['print=', 'help'])

    except getopt.GetoptError as err:
        print str(err)
        sys.exit(2)

    for o, a in opts:
        if o in ('-p', '--print'):
            string_to_print = a

        if o in ('-h', '--help'):
            usage()
            sys.exit(2)

    if not 'string_to_print' in locals():
        print "-p or --print is not given or string is missing\n"
        usage()
        sys.exit(2)

    print string_to_print


if __name__ == "__main__":
    main()

ARGPARSE Ansatz:

#!/usr/bin/env python

import argparse

def main():
        parser = argparse.ArgumentParser(description="test3.py - A test script.")
        parser.add_argument('-p','--print',dest='string_to_print', required=True, help="String to print")
        args = parser.parse_args()

        print args.string_to_print

if __name__ == "__main__":
        main()

 

Python: Snippet Multiprocessing

Wenn es möglich ist Jobs zu parallelisieren kann man Multiprocessing unter Python verwenden.

#!/usr/bin/env python

import os
from multiprocessing import Pool



def worker(job):
    x, y = job

    result = x ** y

    if hasattr(os, 'getppid'):
        print "parent process pid:", os.getppid()
    print "process pid:", os.getpid()

    print "result is: ", result
    print "---"


if __name__ == '__main__':
    jobs = [(1, 2), (3, 4), (5, 6), (11, 12), (13, 14), (15, 16), (21, 22), (23, 24), (25, 26)]
    pool = Pool(processes=5)

    for job in jobs:
        pool.apply_async(worker, args=(job,))

    pool.close()
    pool.join()

Result:

max@cmkdev:~$ python mp.py 
parent process pid: 19599
process pid: 19600
result is:  1
---
parent process pid: 19599
process pid: 19601
result is:  81
---
parent process pid: 19599
process pid: 19602
result is:  15625
---
parent process pid: 19599
process pid: 19602
result is:  3138428376721
---
parent process pid: 19599
process pid: 19600
result is:  6568408355712890625
---
parent process pid: 19599
process pid: 19600
result is:  122694327386105632949003612841
---
parent process pid: 19599
process pid: 19600
result is:  480250763996501976790165756943041
---
parent process pid: 19599
process pid: 19602
result is:  2220446049250313080847263336181640625
---
parent process pid: 19599
process pid: 19604
result is:  3937376385699289
---

 

Python: Snippet – In einer Datei suchen und Zeilennummern zurückgeben

test.txt in der Gesucht wird nach foobar:

wer
w
erw
erwer
foobar
sfsdfhsdkjfhkjsdf
sdf
sdf
sdf
sdf
sdf
sdflskdjflsdjflksjflksjf
sdfkjsdfjkhskjhffoobardjskfhskdjhfkjsdhfkjshdf
sflksdjfjklsdfjs
dfs
dfs
df
sdf
sdf
dsf

Testscript zum Suchen:

#!/usr/bin/env python

filename = 'test.txt'
search = 'foobar'

with open(filename) as f:
    for num, line in enumerate(f, 1):
        if search in line:
            print '%s - found at line:' % search, num



Resultat:

dev1@cmkdev1:/home/dev1$ python test.py 
foobar - found at line: 5
foobar - found at line: 13

 

Check_MK: Cisco MLS QoS Check

Cisco MLS QoS Check ist ein Check_MK kompatibler Check zum Überwachen der der Hardware Queues auf Cisco Switches.

Per Default werden keine Queues / Thresholds überwacht, diese müssen durch eine WATO Rule definiert werden.

Für jedes Interfaces wird ein Service angelegt, mehrere Queues auf einem Interface werden in einem Service dargestellt.

Die Queues werden einzeln als Graphen dargestellt:

 

 

Check_MK: Livestatus Schnittstelle

Check_MK stellt alle Informationen per Livestatus Schnittstelle bereit. So gut wie alles was man auf der Webseite des Monitorings sehen kann kommt aus Livestatus. Der Sprachsyntax für Abfragen ist LQL -> Livestatus Query Language.

Per Default ist die Livestatus Schnittstelle nur auf dem Host selbst über einen Socket abrufbar. Es gibt aber die Möglichkeit per xinetd die Schnittstelle auch per LAN zur Verfügung zu stellen.

Livestatus Schnittstelle auf LAN (per TCP) aktivieren

OMD stoppen

OMD[dev1]:~$ omd stop
Removing Crontab...OK
Stopping dedicated Apache for site dev1....OK
Stopping Check_MK Micro Core...killing 19502....OK
Stopping rrdcached...waiting for termination...OK
Stopping mknotifyd...killing 19481...
Stopping Livestatus Proxy-Daemon...killing 19474....OK
Stopping mkeventd...killing 19466....OK

OMD Config starten

OMD[dev1]:~$ omd config

OMD starten

OMD[dev1]:~$ omd start
Starting mkeventd...OK
Starting Livestatus Proxy-Daemon...OK
Starting mknotifyd...OK
Starting rrdcached...OK
Starting Check_MK Micro Core...OK
Starting dedicated Apache for site dev1...OK
Starting xinetd...OK
Initializing Crontab...OK
ACHTUNG
Nach dem Aktivieren der Livestatus Schnittstelle ist sie von jedem Host aus erreichbar! Wenn dies nicht gewünscht ist muss man das in der xinetd Konfig einschränken.

Erlaubte Hosts auf Livestatus einschränken

Dazu muss ~/etc/xinet.d/mk-livestatus angepasst werden beim Parameter only_from, anschließend muss der xinetd Service neu gestartet werden mit „omd restart xinetd“.

service livestatus
{
  type		= UNLISTED
  socket_type	= stream
  protocol	= tcp
  wait		= no

        # limit to 100 connections per second. Disable 3 secs if above.
  cps             = 100 3

        # set the number of maximum allowed parallel instances of unixcat.
        # Please make sure that this values is at least as high as 
        # the number of threads defined with num_client_threads in
        # etc/mk-livestatus/nagios.cfg
        instances       = 500

        # limit the maximum number of simultaneous connections from
        # one source IP address
        per_source      = 250 

        # Disable TCP delay, makes connection more responsive
  flags           = NODELAY
# configure the IP address(es) of your Nagios server here:
  only_from       = 127.0.0.1 10.0.20.1 10.0.20.2

# ----------------------------------------------------------
# These parameters are handled and affected by OMD
# Do not change anything beyond this point.

# Disabling is done via omd config set LIVESTATUS_TCP [on/off].
# Do not change this:
  disable		= no

# TCP port number. Can be configure via LIVESTATUS_TCP_PORT
  port		= 6557

# Paths and users. Manual changes here will break some omd
# commands such as 'cp', 'mv' and 'update'. Do not toutch!
  user		= dev1
  server		= /omd/sites/dev1/bin/unixcat
  server_args     = /omd/sites/dev1/tmp/run/live
# ----------------------------------------------------------
}

Livestatus Schnittstelle nutzen

Libraries und Beispiele von Check_MK

Check_MK hat in seinen DOCs für Python, Perl und C++ Libraries und Beispiele an Board. Zu finden sind Sie unter ~/share/doc/check_mk/livestatus/api/

Hier ein einfaches Beispiel für Python:

#!/usr/bin/env python

import os
import sys
import livestatus

try:
    omd_root = os.getenv("OMD_ROOT")
    socket_path = "unix:" + omd_root + "/tmp/run/live"

except:
    sys.stderr.write("This example is indented to run in an OMD site\n")
    sys.stderr.write("Please change socket_path in this example, if you are\n")
    sys.stderr.write("not using OMD.\n")
    sys.exit(1)

department = "testx"

hosts = livestatus.SingleSiteConnection(socket_path).query_table("GET hosts\nColumns: name\nFilter: custom_variables ~~ TAGS %s" % department)

for host in hosts:
    print host[0]

Test Run:

OMD[dev1]:~$ python live.py 
router

PHP Library

Check_MK hat selbst keine Library für PHP an Board, ich habe aber bereits folgende erfolgreich eingesetzt.

https://github.com/aashley/nagios-livestatus-client

Beispiel von der Github Seite (siehe Link oben)

<?php

use Nagios\Livestatus\Client;

$options = array(
    'socketType' => 'tcp',
    'socketAddress' => '10.253.14.22',
    'socketPort' => '6557',
);

$client = new Client($options);

$response = $client
    ->get('hosts')
    ->column('host_name')
    ->column('state')
    ->execute();

foreach ($response as $host) {
    print $host[0] . ": " . $host[1] . "\n";
}

$response = $client
    ->get('hosts')
    ->column('host_name')
    ->column('state')
    ->executeAssoc();

foreach ($response as $host) {
    print $host['host_name'] . ": " . $host['state'] . "\n";
}

Auf dem Check_MK Host

root@cmkdev:~# echo -e "GET hosts\nColumns: host_name\n" | unixcat /omd/sites/dev1/tmp/run/live
cluster
node1
router
node3
node2
localhost
Hinweis: Auf Ubuntu muss check-mk-livestatus installiert sein damit unixcat zur Verfügung steht.

In der OMD Umgebung

In der OMD Umgebung steht das Tool lq zur Verfügung welches ebenfalls ein unixcat macht.

OMD[dev1]:~$ lq "GET hosts\nColumns: host_name\n" 
cluster
node1
router
node3
node2
localhost

per TCP von Remote Host

Der Standardport ist tcp/6557.

root@cmkdev2:~# echo -e "GET hosts\nColumns: host_name\n" | netcat 10.211.55.13 6557
cluster
node1
router
node3
node2
localhost

Welche Tabellen / Spalten (Columns) gibt es?

Das lässt sich per Livestatus abfragen:

OMD[dev1]:~$ lq "GET columns\nFilter: table = hosts"

description;name;table;type
Whether passive host checks are accepted (0/1);accept_passive_checks;hosts;int
Whether the current problem has been acknowledged (0/1);acknowledged;hosts;int
Type of acknowledgement (0: none, 1: normal, 2: sticky);acknowledgement_type;hosts;int
An optional URL to custom actions or information about this host;action_url;hosts;string
The same as action_url, but with the most important macros expanded;action_url_expanded;hosts;string
Whether checks of the object are enabled (0/1);active_checks_enabled;hosts;int
IP address;address;hosts;string
An alias name for the host;alias;hosts;string
Logical command name for active checks;check_command;hosts;string
Logical command name for active checks, with macros expanded;check_command_expanded;hosts;string
Whether to check to send a recovery notification when flapping stops (0/1), not supported by CMC;check_flapping_recovery_notification;hosts;int
Whether freshness checks are enabled (0/1);check_freshness;hosts;int
Number of basic interval lengths between two scheduled checks;check_interval;hosts;float
The current check option, forced, normal, freshness (0-2), not supported by CMC;check_options;hosts;int
Time period in which this object will be checked. If empty then the check will always be executed.;check_period;hosts;string
Type of check (0: active, 1: passive);check_type;hosts;int
Whether checks of the object are enabled (0/1);checks_enabled;hosts;int
A list of all direct children of the host;childs;hosts;list
... gekürzt ...

Hilfe beim Zusammenstellen eines LQL Query

Da LQL Queries sehr komplex werden können kann man das Check_MK zur Hilfe nehmen. Dazu muss in den Global Settings „Debug Livestatus queries“ aktivieren.

Anschließend kann man sich einen View zusammenstellen mit den gewünschten Daten, der LQL Query wird unten an der Webseite angezeigt der für die Abfrage verwendet wurde.

Mehr dazu bei Check_MK / Mathias Kettner

Mathias Kettner hat Livestatus entwickelt und bietet auch eine sehr gute Dokumentation dazu an:

https://mathias-kettner.de/checkmk_livestatus.html

Have fun 😉

 

Python: Cisco Config grabber – Suchen von Interface Configs & Erzeugen von neuer Config

Das Script Snippet dient dazu in den Backupfoldern z.B. vom 20.09.2016 nach Interfaces mit einer bestimmten Description und einem gesetzten Parameter z.B. service-policy zu suchen und automatisch Konfiguration zu erzeugen die diese Config entfernt.

Configbeispiel Cisco Config  (swt70/20.09.2016/swt70-config.txt):

interface GigabitEthernet1/0/47
 description acp10 
 switchport access vlan 17
 switchport mode access
 switchport nonegotiate
 spanning-tree guard loop
 service-policy input ACCESSPOINT
!
interface GigabitEthernet1/0/48
 description acp43 
 switchport access vlan 17
 switchport mode access
 switchport nonegotiate
 spanning-tree guard loop
 service-policy input ACCESSPOINT
!
Das Script benötigt CiscoConfParse und es kann mit „pip install ciscoconfparse“ installiert werden.
#!/usr/bin/python

import os
from ciscoconfparse import CiscoConfParse
import re

buffer = []
buffer_f = []

# Folderstruktur: <switchname>/<backup_datum>/config.txt
for dir in os.listdir("."):
        buffer.append("./"+dir+"/20.09.2016/")

# Files zusammensammeln
for x in buffer:
        try:
                for f in os.listdir(x):
                        buffer_f.append(x+f)
        except:
                pass


# Alle files abarbeiten
for f in buffer_f:
  # Parser laden
  parse = CiscoConfParse(f)
  
  # Interfaces finden in config
  all_intfs = parse.find_objects(r"^interf")
  intfs = list()
  fault_intfs = list()
  # in den interfaces nach description .+acp.+ suchen
  for obj in all_intfs:
    if obj.re_search_children(r"description.+acp.+|description.+ACP.+"):
      intfs.append(obj)
  
  # fuer alle Suchergebnisse description .+acp.+ suche nach service-policy
  for i in intfs:
    
    if i.re_search_children(r"service-policy.+"):
      sp = i.re_search_children(r"service-policy.+")
      
      fault_intfs.append((i, sp[0].text) )
  
  # wenn eine zeile im fault buffer ist 
  if len(fault_intfs) > 0:
    
    # switchnamen aus dateinamen extrahieren
    n = re.search(r"^.+(swt[0-9][0-9]).+|^.+(SWT[0-9][0-9]).+",f)
    if n.group(1) is None:
      print n.group(2)
    else:
      print n.group(1)
    
    # no statements erzeugen
    for i, no_cmd in fault_intfs:
      print "!"
      print i.text
      print "no "+no_cmd
      
    print "! ---"
  else:
    pass

Das Script erzeugt folgenden Output:

swt70
!
interface GigabitEthernet1/0/47
no  service-policy input ACCESSPOINT
!
interface GigabitEthernet1/0/48
no  service-policy input ACCESSPOINT
! ---

 

Check_MK: Cluster Checks bauen

Hier ein kleines Beispiel wie man einen Cluster Check implementieren kann.

Testumgebung:

3 Nodes node1,node2 und node3 welche als Clusterobjekt cluster zusammengefasst sind. Die Daten kommen von Piggybacks.

Clustercheck ist mycluster und ist ein Beispiel Check.

Schritt 1: Nodes anlegen

In WATO 3 Nodes anlegen mit der IP 127.0.0.1 (Testumgebung, Daten kommen von Piggybacks), Agent: No Agent 

Schritt 2: Clusterobjekt anlegen

Schritt 4: Clustercheck bauen, Piggybacks & Inventory

Unser eigener Check wird in ~/local/share/check_mk/checks/ abgelegt, er heisst mycluster.

#!/usr/bin/env python

def inventory_mycluster(info):
    for line in info:
        #pprint.pprint(line)
        if "CLUSTER" in line[0]:
            return [(None, None)]


def check_mycluster(item, params, info):
    active_nodes = 0
    for node in info:
        key, value = node
        if "CLUSTER" in key:
            if "ACTIVE" in value:
                active_nodes += 1

    if active_nodes <= 1:
        status = 2
    else:
        status = 0
    return status, "%d active nodes" % active_nodes


check_info['mycluster'] = {
    'inventory_function'    : inventory_mycluster,
    'check_function'        : check_mycluster,
    'service_description'   : 'MYCluster',
}

Piggybacks ablegen in /var/lib/check_mk_agent/spool/mycluster_test

<<<<node1>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node2>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node3>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>
<<<<node4>>>>
<<<mycluster>>>
CLUSTER	ACTIVE
<<<<>>>>

Inventory durchführen:

OMD[dev1]:~/local/share/check_mk/checks$ cmk -v -II cluster
Discovering services on cluster:
node1:
Using piggyback information from host localhost.
    1 mycluster

node2:
Using piggyback information from host localhost.
    1 mycluster

node3:
Using piggyback information from host localhost.
    1 mycluster

Reload durchführen:

OMD[dev1]:~/local/share/check_mk/checks$ cmk -R
Generating configuration for core (type cmc)...OK
Packing config...OK
Restarting monitoring core...OK

Schritt 5: Service zum Cluster Service machen

Eine neue WATO Regel erstellen „Clustered services“:

WATO Änderung aktivieren, die Services verschwinden bei den Nodes und werden nur noch unter dem Cluster Objekt angezeigt.

 

 

Viel Spaß 🙂

DNS Blacklist Check

Check um zu Überprüfen ob ein Mailserver auf einer Blacklist ist. Der Check lässt sich auch in Check_MK einbinden, dazu das Script in ~/local/lib/nagios/plugins/ ablegen und eine Regel „Classical active and passive Monitoring checks“ für den Mailserver erstellen. Eine native Check_MK Implementierung folgt noch 😉

#!/usr/bin/env python
# -*- encoding: utf-8; py-indent-offset: 4 -*-

#
# check_dnspl.py - Check IP against Blacklist
# Use it on your own risk!
#
# Written 2017 - Maximilian Thoma
#
# This program is free software; you can redistribute it and/or modify it under the terms of the GNU
# General Public License as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with this program; if not,
# write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
#

import getopt
import sys
import socket

# Define blacklists to be checked
blacklists = [
    'all.s5h.net',
    'b.barracudacentral.org',
    'bl.emailbasura.org',
    'bl.spamcannibal.org',
    'bl.spamcop.net',
    'blacklist.woody.ch',
    'bogons.cymru.com',
    'cbl.abuseat.org',
    # 'cdl.anti-spam.org.cn',
    'combined.abuse.ch',
    'db.wpbl.info',
    'dnsbl-1.uceprotect.net',
    'dnsbl-2.uceprotect.net',
    'dnsbl-3.uceprotect.net',
    'dnsbl.anticaptcha.net',
    'dnsbl.cyberlogic.net',
    'dnsbl.dronebl.org',
    'dnsbl.inps.de',
    'dnsbl.sorbs.net',
    'drone.abuse.ch',
    'drone.abuse.ch',
    'duinv.aupads.org',
    'dul.dnsbl.sorbs.net',
    'dyna.spamrats.com',
    'dynip.rothen.com',
    'exitnodes.tor.dnsbl.sectoor.de',
    'http.dnsbl.sorbs.net',
    'ips.backscatterer.org',
    'ix.dnsbl.manitu.net',
    'korea.services.net',
    'misc.dnsbl.sorbs.net',
    'noptr.spamrats.com',
    'orvedb.aupads.org',
    'pbl.spamhaus.org',
    'proxy.bl.gweep.ca',
    'psbl.surriel.com',
    'relays.bl.gweep.ca',
    'relays.nether.net',
    'sbl.spamhaus.org',
    'short.rbl.jp',
    'singular.ttk.pte.hu',
    'smtp.dnsbl.sorbs.net',
    'socks.dnsbl.sorbs.net',
    'spam.abuse.ch',
    'spam.dnsbl.sorbs.net',
    'spam.spamrats.com',
    'spambot.bls.digibase.ca',
    'spamrbl.imp.ch',
    'spamsources.fabel.dk',
    'ubl.lashback.com',
    'ubl.unsubscore.com',
    'virus.rbl.jp',
    'web.dnsbl.sorbs.net',
    'wormrbl.imp.ch',
    'xbl.spamhaus.org',
    'z.mailspike.net',
    'zen.spamhaus.org',
    'zombie.dnsbl.sorbs.net',
]


def check_if_valid_host_ip(ip):
    try:
        socket.inet_aton(ip)
        return True
    except socket.error:
        return False


def revert_ip(ip):
    x = ip.split('.')
    return x[3] + '.' + x[2] + '.' + x[1] + '.' + x[0]


def bls(olist):
    x = ''
    for bl in olist:
        x += bl + " "
    return x


def log(debug, s):
    if debug:
        print s


def usage():
    print "check_dnsbl.py - Check IP against DNS blacklists.\n" \
          " -H, --host <hostname or ip> Hostname or IP\n" \
          " -d, --debug Debug Modus\n" \
          " -h, --help Help"


def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "H:dh", ['host=', 'debug', 'help'])
    except getopt.GetoptError as err:
        print str(err)
        sys.exit(2)

    found_h = False
    host = None
    debug = False

    for o, a in opts:
        if o in ('-H', '--host'):
            host = a
            found_h = True
        if o in ('-d', '--debug'):
            debug = True
        if o in ('-h', '--help'):
            usage()
            sys.exit(2)

    if not found_h:
        print "-H is not given"
        usage()
        sys.exit(2)

    # print host
    # print debug



    # Check if valid Host IP
    if check_if_valid_host_ip(host) is not True:
        try:
            resolved_ip = socket.gethostbyname(host)
        except socket.gaierror:
            sys.stderr.write('Unable to make an DNS lookup, provided IP or hostname is invalid.')
            sys.exit(2)
        if check_if_valid_host_ip(resolved_ip) is not True:
            sys.stderr.write('Error no valid IP address.')
            sys.exit(2)
        else:
            ip = resolved_ip
    else:
        ip = host

    # Revert IP
    rip = revert_ip(ip)

    # Init variables
    negative_result_buffer = []

    for bl in blacklists:
        # Init Result
        result = ''
        # Build query string
        q = rip + '.' + bl
        log(debug, q)

        # Query DNS
        try:
            result = socket.gethostbyname(q)
            log(debug, "Result: %s" % result)
        except socket.error:
            log(debug, "No result")
            pass

        if "127.0.0" in result:
            log(debug, "Found 127.0.0 in result.")
            negative_result_buffer.append(bl)

    if len(negative_result_buffer) == 0:
        print "OK - %s (%s) is not listed at: %s" % (host, ip, bls(blacklists))
        sys.exit(0)
    else:
        print "CRITICAL - %s (%s) ist listed at: %s" % (host, ip, bls(negative_result_buffer))
        sys.exit(2)


if __name__ == "__main__":
    main()

 

Python: Einfacher IMAP Client

Die Überschrift sagt eigentlich schon alles. Das Code Snippet dient dazu von einem IMAP Server Mails zu laden und mit dem Payload Dinge anzustellen.

#!/usr/bin/env python

import imaplib
import sys
import email
import re
import email.header
import base64
from HTMLParser import HTMLParser

username = "blah@blah.de"
password = "xxxxxx"

imap_rz1 = "imap.xxx.de"
imap_rz2 = "imap-rz2.xxxxx.de"


class MLStripper(HTMLParser):
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_data(self):
        return ''.join(self.fed)

def strip_tags(html):
    s = MLStripper()
    s.feed(html)
    return s.get_data()




# Try primary server
try:
    M = imaplib.IMAP4(imap_rz1)

except:

    sys.stderr.write("Try second server, primary not reachable.\n")
    # Try secondary server
    try:
        M = imaplib.IMAP4(imap_rz2)

    except:
        sys.stderr.write("Error no connection possible.\n")
        sys.exit(1)

# Try to Login
try:
    M.login(username, password)

except:
    sys.stderr.write("Error login.\n")
    sys.exit(1)

# DATA
M.select()
typ, data = M.search(None, 'ALL')

for num in data[0].split():
    typ, data = M.fetch(num, '(RFC822)')

    #### print 'Message %s\n%s\n' % (num, data[0][1])
    msg = email.message_from_string(data[0][1])
    hdr = email.header.make_header(email.header.decode_header(msg['Subject']))

    if msg.is_multipart():
        print "Multipart Mail"
        for payload in msg.get_payload():
            print payload.get_payload()
    else:

        enc = msg['Content-Transfer-Encoding']

        if enc == "base64":
            dirty = msg.get_payload()
            payload = base64.decodestring(dirty)
            print payload
            #print strip_tags(payload)
            pmail = parse_mail(mailcontent=strip_tags(payload).rstrip().lstrip().splitlines())
        else:
            print msg.get_payload()
            #print strip_tags(msg.get_payload())
            pmail = parse_mail(mailcontent=strip_tags(msg.get_payload()).rstrip().lstrip().splitlines())
            
    # Set to deleted
    M.store(num, '+FLAGS', '\\Deleted')

# Delete permanently
M.expunge()
# Close current mailbox
M.close()
# Logout
M.logout()

Check_MK: Host custom variables

Check_MK bietet auch die Möglichkeit die Host Variablen zu erweitern um z.B. ein Host Description Feld hinzuzufügen.

Die Erweiterung muss in ~/local/share/check_mk/web/plugins/wato/ abgelegt werden. Die Dateiendung ist zwingend .py.

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ cat host_custom_vars.py 

#!/usr/bin/env python

declare_host_attribute(
    NagiosTextAttribute(
        # Sektion / Gruppe
        "MY_HOST_CUSTOM_VARIABLES",
        # Variable mit beginnenden Unterstrich
        "_HOST_MAGIC",
        # Feldbeschreibung
        _("Host Magic"),
        # Hilfe
        _("Hilfe"),
    ),
    show_in_table=True,
    show_in_folder=True,
)

Nach dem Anlegen eines eigenen Attributs muss der Apache neu gestartet werden mit z.B. „omd restart apache“.

Die Host custom variables sind auch per Livestatus abrufbar:

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ lq "GET hosts\nColumns: host_name host_address\nFilter: host_custom_variables ~ HOST_MAGIC (^|[ ])magic($|[ ])"
localhost;127.0.0.1

 

Check_MK: Service custom variables

Die Service Variablen lassen sich genau so Erweitern wie die Host Variablen.

Dazu muss man eine neue Definition erstellen im Folder ~/local/share/check_mk/web/plugins/wato/. Die Dateiendung muss .py sein.

#!/usr/bin/env python

register_rule(
    # Sektion
    "Test",
    # Variable
    "extra_service_conf:_test_test",
    # Parameter
    TextUnicode(
        title = _("TEST TEST"),
        help = _("Test test"),
        size = 80,
        attrencode = True,
    ),
    itemtype = 'service',
    match = 'first',
)

Die Variable in der das gespeichert wird ist: extra_service_conf:_test_test, in Livestatus ist es dann unter service_custom_variables als TEST_TEST abrufbar.

Danach ein „omd restart apache && cmk -R“ um die Änderung zu aktivieren.

Jetzt kann mann unter „Parameters for this service“ einen Wert für den Service setzen.

Die selbst festgelegten Parameter können auch per Livestatus abgefragt werden:

OMD[dev1]:~/local/share/check_mk/web/plugins/wato$ lq "GET services\nColumns: service_host_name service_service_description\nFilter: service_custom_variables ~ TEST_TEST (^|[ ])test123($|[ ])"
localhost;CPU utilization

 

Check_MK: Script um IP Adresse statisch bei einem Host einzutragen

Wenn die Hosts nur mit DNS-Namen angelegt sind wird regelmäßig der DNS-Cache aktualisiert, dies verzögert den Deploy Prozess. Das Script ließt den DNS Cache aus und setzt für die Hosts die IP Adresse statisch, ist die IP nicht im Cache löst er diese direkt per DNS auf.

#!/usr/bin/env python

#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but WITHOUT ANY 
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with 
# this program; if not, write to the Free Software Foundation, Inc., 
# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
#

import json
import os
import subprocess
import sys
import livestatus
import socket

url = "http://cmkdev/dev1/check_mk/webapi.py"
user = "checkmk_automation"
passw = "password"
department = "hosttag"

##########################################################################################
### DON'T CHANGE ANYTHING BELOW
##########################################################################################

curl_base_cmd = url + "?_username=%s&_secret=%s" %(user,passw)

def execute_curl_cmd(curl_cmd):
    try:
        p = subprocess.Popen(curl_cmd, shell=True, stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        o, e = p.communicate()
        out  = json.loads(o)
    except Exception, e:
        sys.stderr.write("ERROR: '%s' while executing '%s'\n" % (e, curl_cmd))
        sys.exit(1)
        return {}

    if out["result_code"] == 0:
        return out["result"]
    else:
        sys.stderr.write("ERROR: while executing WEB API command: '%s'\ncurl: '%s'\n" % \
                         (out["result"], curl_cmd))
        sys.exit(1)
        return {}

def edit_wato_host(curl_base_cmd, req):
    create_cmd = ('curl --insecure \'' + curl_base_cmd + \
                  '&action=edit_host\' ' + \
                  '-d \'%s\'') % req
    return execute_curl_cmd(create_cmd)

try:
    omd_root = os.getenv("OMD_ROOT")
    socket_path = "unix:" + omd_root + "/tmp/run/live"
    ip_cache = omd_root + "/var/check_mk/ipaddresses.cache"

    with open(ip_cache,'r') as inf:
        ipc = eval(inf.read())

except:
    sys.stderr.write("This example is indented to run in an OMD site\n")
    sys.stderr.write("Please change socket_path in this example, if you are\n")
    sys.stderr.write("not using OMD.\n")
    sys.exit(1)

hosts_arr = livestatus.SingleSiteConnection(socket_path).query_table("GET hosts\nColumns: name\nFilter: custom_variables ~~ TAGS %s" % department)

for host in hosts_arr:

    cached_ip = ipc.get((host[0], 4))

    if cached_ip != None:

        print host[0]
        print cached_ip

        req = 'request={"hostname": "%s", "attributes":{"ipaddress": "%s"}}' %(host[0],cached_ip)

        print req

        edit_wato_host(curl_base_cmd,req)

    else:

        try:
            ip = socket.gethostbyname(host[0])
            print "%s - %s" % (host[0],ip)

        except:
            print "no dns record for host %s " % host[0]

        req = 'request={"hostname": "%s", "attributes":{"ipaddress": "%s"}}' %(host[0],ip)

        print req

        edit_wato_host(curl_base_cmd,req)

 

Check_MK: yield statt return in der Check Funktion

Check_MK erwartet bei der Check Funktion ein Tuple mit 2 (ohne Performance Daten) bzw. 3 Werten (mit Performance Daten).

<STATUS>, <MESSAGE>, <PERFOMANCEDATA>

Wenn man in einem Service mehrere Werte aggregieren möchte z.B. man hat mehrere Lüfter im System möchte aber nicht für jeden einen eigenen Service sondern nur einen einzigen, so muss man die Werte von mehreren vergleichen und dann den entsprechenden Status zurückgeben. Da diese ganzen Vergleiche aufwändig sind und fehleranfällig löst Check_MK so eine Situation mit dem yield Generator. Diesem werden alle Werte einfach übergeben, er setzt nach einer Logik den Servicestatus und fügt alle einzelnen Status in den Output des Services.

Der Status des Checks wird in folgender Reihenfolge angezeigt, d.H. ist einer der Status UNKNOWN wird der komplette Service als UNKNOWN angezeigt:

  1. Unknown
  2. Critical
  3. Warn
  4. OK

Beispiel alte Lösung mit 1 return

def check_mycheck(item, params, info):
    warn = False
    crit = False
    unkn = False

    msg = ""

    for i in info:
        fan, status = i
        status = int(status) 

        if status == 2:
            crit = True
            msg += " %s: CRIT -" % fan
        elif status == 1:
            warn = True
            msg += " %s: WARN -" % fan
        elif status == 3:
            unkn = True
            msg += " %s: UNKN -" % fan
        else:
            msg += " %s: OK -" % fan

    if unkn == True:
        return 3, msg
    elif crit == True:
        return 2, msg
    elif warn == True:
        return 1, msg
    else:
        return 0, msg

Ausgabe:

Beispiel neue Lösung mit yield

def check_mycheck(item, params, info):
    for i in info:
        fan, status = i
        status = int(status)
        yield status, "Status von: %s" % fan

Ausgabe:

Check_MK: Piggyback Checkergebnisse anderer Hosts bereitstellen

Es gibt die Möglichkeit Check Ergebnisse wenn ein Host nicht direkt erreichbar ist für Check_MK oder man bestimmte Dinge z.B. Erreichbarkeit eines TCP Services von einem anderen Host aus testen möchte, als Piggyback über den Agenten eines anderen Hosts mitzugeben.

In den Beispiel haben wir einen Host (router) der ein Check Ergebnis von einem anderen Host (localhost) bekommen soll. Check_MK verwendet hierfür folgenden Syntax:

<<<<hostname>>>>

Check Ergebnisse, Ausgaben von Local Checks, etc.

<<<<>>>>

Das Piggyback File muss auf dem Host mit Agenten unter /var/lib/check_mk_agent/spool abgelegt werden. Ich habe die Ausgabe für ein Agent Check + einen Local Check eingebaut.

root@cmkdev:/var/lib/check_mk_agent/spool# cat piggy_router 

<<<<router>>>>
<<<mycheck>>>
foo 1
bar 2

<<<local>>>
0 Mein_piggyback_localcheck - TEST TEST TEST

<<<<>>>>

Dazu ein einfacher Check der in der Zeile den 2 Parameter als Status für den ersten zurückgibt:

OMD[dev1]:~/local/share/check_mk/checks$ cat mycheck 
#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-


def inventory_mycheck(info):
    if len(info) > 1:
        yield '', None


def check_mycheck(item, params, info):
    for i in info:
        yield int(i[1]), "Status von: %s" % i[0]



check_info['mycheck'] = {
    'inventory_function'    : inventory_mycheck,
    'check_function'        : check_mycheck,
    'service_description'   : 'MyCheck',
}

Inventory, Reload und Test:

OMD[dev1]:~$ cmk -v -II router
Discovering services on router:
router:
Using piggyback information from host localhost.
    1 local
    1 mycheck

OMD[dev1]:~$ cmk -R
Generating configuration for core (type cmc)...OK
Packing config...OK
Restarting monitoring core...OK

OMD[dev1]:~$ cmk -v router
Check_MK version 1.4.0p8
Using piggyback information from host localhost.
Mein_piggyback_localcheck OK - TEST TEST TEST
MyCheck              CRIT - Status von: foo(!), Status von: bar(!!)
OK - execution time 0.0 sec|execution_time=0.002 user_time=0.000 system_time=0.010 children_user_time=0.000 children_system_time=0.000

Check_MK weist auf der Shell darauf hin das Piggyback Informationen verwendet wurden:

Using piggyback information from host localhost.

 

Viel Spaß beim piggybacken 😉

Check_MK: Mit gespeicherten SNMP Walks Checks entwickeln

Es ist keine schlechte Idee seine Checks auf einer Testinstanz zu entwickeln, geht etwas schief wird das Produktionssystem davon nicht beeinflusst.

Check_MK bietet die Möglichkeit einen SNMP Walk zu exportieren und diesen für die Checkentwicklung zu verwenden.

Schritt 1: Export eines SNMP Walks in Check_MK

OMD[test1]:~$ cmk -v --snmpwalk router
router:
Walk on ".1.3.6.1.2.1"...2939 variables.
Walk on ".1.3.6.1.4.1"...1539 variables.
Successfully Wrote /omd/sites/test1/var/check_mk/snmpwalks/router.

Mit dem Befehl „cmk –snmpwalk <hostname>“ lässt sich auf dem Produktivsystem ein Check_MK kompatibler SNMP Walk erzeugen der in der Site unter ~/var/check_mk/snmpwalks/ abgelegt wird.

Schritt 2: Check_MK Testinstanz erzeugen

root@max:~# omd create dev
Adding /opt/omd/sites/dev/tmp to /etc/fstab.
Creating temporary filesystem /omd/sites/dev/tmp...OK
Restarting Apache...OK
Created new site dev with version 2017.07.11.cee.

  The site can be started with omd start dev.
  The default web UI is available at http://max/dev/

  The admin user for the web applications is cmkadmin with password: xxxxxxxxxxx
  (It can be changed with 'htpasswd -m ~/etc/htpasswd cmkadmin' as site user.
)
  Please do a su - dev for administration of this site.

root@max:~# omd start dev
Starting mkeventd...OK
Starting liveproxyd...OK
Starting mknotifyd...OK
Starting rrdcached...OK
Starting cmc...OK
Starting apache...OK
Initializing Crontab...OK
root@max:~#
  1. omd create dev
  2. omd start dev

Schritt 3: Kopieren des Walks

root@max:~# cp /omd/sites/prod/var/check_mk/snmpwalks/router /omd/sites/dev/var/check_mk/snmpwalks/router

Schritt 4: Host anlegen & SNMP walk Regel erstellen

Der Host muss genau so heissen wie das Walk-File, als IP-Adresse 127.0.0.1 angeben und bei Agent Type SNMP.

Anschließend noch eine Regel erstellen das die Walks verwendet werden für den Host.

Danach die Änderungen deployen.

Schritt 5: Inventory durchführen & Aktivieren

  1. Inventory: cmk -v -II router
  2. Aktivieren: cmk -v -O
  3. cmk -v -D router
OMD[dev]:~$ cmk -v -II router
Discovering services on router:
router:
    1 hr_cpu
    4 hr_fs
    1 hr_mem
    2 if64
    1 snmp_info
    1 snmp_uptime
    1 ucd_cpu_load

OMD[dev]:~$ cmk -v -O
Waiting for exclusive lock on /omd/sites/dev/etc/check_mk/main.mk.
Generating configuration for core (type cmc)...
Not importing state from Nagios, /omd/sites/dev/var/nagios/retention.dat not found.
/omd/sites/dev/var/check_mk/core/config written.
OK
Baking agents...VANILLA...linux_rpm:uptodate...linux_deb:uptodate...linux_tgz:uptodate...aix_tgz:uptodate...solaris_tgz:uptodate...solaris_pkg:uptodate...windows_msi:uptodate...OK
GENERIC...linux_rpm:uptodate...linux_deb:uptodate...linux_tgz:uptodate...aix_tgz:uptodate...solaris_tgz:uptodate...solaris_pkg:uptodate...windows_msi:uptodate...OK
router...does not use agent, skipping.
OK
Packing config...OK
Reloading monitoring core...OK
OMD[dev]:~$

OMD[dev]:~$ cmk -D router

router                                                                         
Addresses:              127.0.0.1
Tags:                   /wato/, ip-v4, ip-v4-only, lan, prod, site:dev, snmp, snmp-only, wato
Host groups:            
Contact groups:         all
Type of agent:          SNMP (use stored walk)
Services:
  checktype    item     params                                                                                                                                                                                                                        description         groups
  ------------ -------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------- ------
  ucd_cpu_load None     (5.0, 10.0)                                                                                                                                                                                                                   CPU load                  
  hr_cpu       None     {'levels': (80.0, 90.0)}                                                                                                                                                                                                      CPU utilization           
  hr_fs        /        {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /              
  hr_fs        /boot    {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /boot          
  hr_fs        /distros {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /distros       
  hr_fs        /opt/omd {'trend_range': 24, 'show_levels': 'onmagic', 'inodes_levels': (10.0, 5.0), 'magic_normsize': 20, 'show_inodes': 'onlow', 'levels': (80.0, 90.0), 'show_reserved': False, 'levels_low': (50.0, 60.0), 'trend_perfdata': True} Filesystem /opt/omd       
  if64         2        {'state': [u'1'], 'errors': (0.01, 0.1), 'speed': 1000000000}                                                                                                                                                                 Interface 2               
  if64         3        {'state': [u'1'], 'errors': (0.01, 0.1), 'speed': 100000000}                                                                                                                                                                  Interface 3               
  hr_mem       None     (150.0, 200.0)                                                                                                                                                                                                                Memory used               
  snmp_info    None     None                                                                                                                                                                                                                          SNMP Info                 
  snmp_uptime  None     {}                                                                                                                                                                                                                            Uptime                    
OMD[dev]:~$ 

Viel Spaß beim Entwickeln 😉

Check_MK: SNMP based Check

Neben der Möglichkeit lokale Scripts per Agent oder Agent Plugins zu verwenden gibt es noch die Option Werte direkt per SNMP von Geräten auszulesen. Bei diesem Beispiel ist auch eine Erweiterung dabei damit in WATO eine Regel erstellt werden kann mit den Parametern für den Check. Performance Werte sind in dem Beispiel ebenfalls integriert.

Die anderen HowTos sind unter Agent Plugin Check und Local Check zu finden.

 

Aufbau eines SNMP Checks

Der SNMP Check besteht genau so wie der Agent based Check aus drei Bestandteilen.

  1. Inventory Funktion
  2. Check Funktion
  3. check_info Directory

Hier ein Beispiel Check dazu, der Check wird abgelegt in ~/local/share/check_mk/checks/:

#!/usr/bin/python

default_mycanon = (500,20000)

def inventory_mycanon(info):

    for i in info:
        idx, frag, pages = i
        yield idx, default_mycanon


def check_mycanon(item, params, info):

    perf=[]

    for i in info:
        idx, frag, pages_s = i

        if idx == item:
            warn, crit = params
            pages = int(pages_s)

    perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]


    if pages >= crit:
        status = 2
    elif pages >= warn:
        status = 1
    else:
        status = 0

    return status, "%d pages (w:%d/c:%d) %s" % (pages, warn, crit, get_item_state("buh")), perf


check_info["mycanon"] = {
    "inventory_function":   inventory_mycanon,
    "check_function":       check_mycanon,
    "service_description":  "Printer FooBar",
    "snmp_scan_function":   lambda oid: "canon" in oid(".1.3.6.1.2.1.1.1.0").lower(),
    "snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", "3", "4"]),
    "has_perfdata":         True,
    "group":                "mycanon",
}

Im Gegensatz zu den anderen Checks beginne ich in umgekehrter Reihenfolge mit den einzelnen Bausteinen weil in dem check_info Dictionary definiert ist wo die Inventory Funktion und die Check Funktion ihre Daten her bekommen.

Das check_info Dictionary

check_info["mycanon"] = {
    "inventory_function":   inventory_mycanon,
    "check_function":       check_mycanon,
    "service_description":  "Printer FooBar",
    "snmp_scan_function":   lambda oid: "canon" in oid(".1.3.6.1.2.1.1.1.0").lower(),
    "snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", "3", "4"]),
    "has_perfdata":         True,
    "group":                "mycanon",
}

Genauso wie bei dem Agent Check muss auch beim SNMP Check die Inventory Funktion und die Check Funktion angegeben werden. Die Service Description ist der Name unter dem später der Check in Check_MK im Host auftaucht.

Die snmp_scan_function wird verwendet um festzustellen ob Check_MK überhaupt auf diesem Gerät tätig wird. Nur wenn der Scan erfolgreich ist wird per SNMP BULK WALK die OIDs die in snmp_info angegeben sind abgerufen.

Bei dem Testgerät handelt es sich um einen Canon Drucker, bei einem SNMP Get auf die OID .1.3.6.1.2.1.1.1.0 gibt er folgenden String zurück „Canon iR C1021 /P“. Die Lambda Funktion überprüft ob in der OID .1.3.6.1.2.1.1.1.0 der String „canon“ vorkommt. Dabei wird die Rückgabe von oid(„.1.3.6.1.2.1.1.1.0“) mit lower() in Lowercase umgewandelt um den Vergleich zu vereinfachen.Hier gibt es noch verschiedene andere Möglichkeiten um zu bestimmen ob der Check zum Gerät passt. Am besten sich dazu die Check_MK eigenen Checks ansehen unter: ~/share/check_mk/checks

In snmp_info werden die OIDs angegeben die Abgerufen werden. Wenn es nur eine OID ist so ist ein Tuple mit OID und den entsprechenden einzelnen Untertabellen anzugeben. Sind es mehrere OIDs sind die Tuples in eine Liste zu verpacken.

"snmp_info":            ( ".1.3.6.1.4.1.1602.1.11.1.4.1", ["2", 
                                                           "3",
                                                           "4"
                        ]),

In dem Beispiel rufen wir jetzt drei Untertabellen der OID .1.3.6.1.4.1.1602.1.11.1.4.1 ab. Diese werden an den Check für die Inventory Funktion und die Check Funktion als verschachtelte Liste übergeben.

Um das zu Testen geben wir die info in der Inventory Funktion mit pprint aus:

def inventory_mycanon(info):
    pprint.pprint(info)

Ausgabe auf der Shell:

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II printer-canon-c1021
Discovering services on printer-canon-c1021:
printer-canon-c1021:
[[u'101', u'', u'1'],
 [u'102', u'', u'1'],
 [u'104', u'', u'1'],
 [u'108', u'', u'8930'],
 [u'113', u'', u'8930'],
 [u'114', u'', u'2994'],
 [u'123', u'', u'11147'],
 [u'127', u'', u'18618'],
 [u'133', u'', u'7877'],
 [u'148', u'', u'10741'],
 [u'201', u'', u'1459'],
 [u'202', u'', u'1459'],
 [u'222', u'', u'1053'],
 [u'230', u'', u'406'],
 [u'231', u'', u'406'],
 [u'232', u'', u'406']]

In der Liste sind weitere Listen die den Werten aus der SNMP Anfrage entsprechen. Aus der ersten Liste [u’101′, u“, u’1′] ist der 1. Wert aus der 2. Untertabelle, der 2. Wert aus der 3. Untertabelle und der 3. Wert aus der 4. Untertabelle. (siehe oben bei snmp_info)

Inventory Funktion

default_mycanon = (500,20000)

def inventory_mycanon(info):

    for i in info:
        idx, frag, pages = i
        yield idx, default_mycanon

Die Inventory Funktion erkennt neue Services und legt diese in Check_MK an. Der Funktion wird das SNMP BULK WALK Ergebnis übergeben als Liste. (siehe oben) Diese einfache Inventory Funktion legt für jede Subliste einen neuen Service an. Dem yield Generator wird ein Tuple übergeben mit eindeutigem Servicenamen und den Default Parametern für den Check.

Check Funktion

def check_mycanon(item, params, info):

    perf=[]

    for i in info:
        idx, frag, pages_s = i

        if idx == item:
            warn, crit = params
            pages = int(pages_s)

    perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]


    if pages >= crit:
        status = 2
    elif pages >= warn:
        status = 1
    else:
        status = 0

    return status, "%d pages (w:%d/c:%d)" % (pages, warn, crit), perf

Die Check Funktion beinhaltet die Logik welche überprüft in welchem Zustand sich ein Service befindet (0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN).

Der Check Funktion werden 3 Variablen übergeben: item, params, info

Variable item

Der Variable wird der Servicename übergeben

Variable params

Hier werden die Parameter übergeben die beim Inventory oder durch eine WATO Regel für den Service gesetzt sind.

Variable info

Hier wird die komplette Ausgabe des Agent Check Plugins als Liste übergeben so wie bei der Inventory Funktion.

Schritt 1: Aus info Liste das richtige Item suchen

for i in info:
       idx, frag, pages_s = i

       if idx == item:
           warn, crit = params
           pages = int(pages_s)

Die Liste wird durchgegangen und mit einer IF Anweisung wird verglichen ob es sich um den richtigen Datensatz handelt, wenn True wird das Parameter Tuple in die Variablen warn und vrit gespeichert. Der dritte Wert der Liste ist die Seitenanzahl und die muss noch von einem Unicode String umgewandelt werden zu einem Integer int(pages_s).

Schritt 2: Performance Werte

perf = [("pages",pages, warn, crit), ("page_rate",get_rate("page_rate", time.time(), pages), warn, crit)]

Die Performance Werte werden als Liste übergeben. In der Liste sind wiederum Tupels.

Der Tuple ist wie folgt aufgebaut:

(„<name_des_counters>“, <aktueller_wert>, <warn_wert>, <crit_wert>)

Nettes Feature in Check_MK:

Es gibt die Funktion get_rate welche Werte speichern kann und eine Rate pro Sekunde errechnen kann. Dazu ist eine eindeutige Identifier zu bestimmen und es muss die aktuelle Zeit mitgegeben werden damit Check_MK die Rate berechnen kann. Das gleiche gibt es auch für Average Werte, auch ist es möglich Werte ohne Berechung einfach zu speichern bis zur nächsten Check Ausführung (set_item_state(), get_item_state()).

Schritt 3: Status Logik

Der Status des Checks wird als 0 = OK, 1 = WARNING, 2 = CRITICAL oder 3 = UNKNOWN übergeben.

if pages >= crit:
    status = 2
elif pages >= warn:
    status = 1
else:
    status = 0

Schritt 4: Return oder Yield des Checkergebnisses

return status, "%d pages (w:%d/c:%d)" % (pages, warn, crit), perf

Als letztes wird ein Tuple zusammengebaut das aus 3 Werten besteht:

STATUS, MESSAGE, PERFORMANCEVALUES

Es gibt die Möglichkeit auch das dem yield Generator zu übergeben, statt return wird dann yield verwendet. Check_MK aggregiert dann alle Ergebnisse nacheinander. Dazu kommt in einem anderen Artikel noch weitere Informationen.

WATO Erweiterung für eigene Parameter

Ich habe in unserem Beispiel default Parameter definiert die für jeden Check angelegt werden. Wenn man jetzt für einen Host, Service oder Host/Service die Werte anpassen möchte und es bequem über WATO erledigen will muss man WATO erweitern.

WATO Erweiterungen befinden sich unter ~/lokal/share/check_mk/web/plugins/wato/ und müssen die Dateiendung .py haben. Auch muss zwingend der Apache neu gestartet werden. Das geht auf der OMD Konsole mit „omd restart apache“.

#!/usr/bin/python

register_check_parameters(
    # Category
    "Printers",
    # Group
    "mycanon",
    # Rulename
    "Printer pages max values",
    #Parameter
    Tuple(
        title = "Printer pages max value",
        help = "Define critical and warning values",
        elements = [
            Integer(title="Warn at", unit="pages"),
            Integer(title="Critical at", unit="pages"),
        ]
    ),
    #Item
    TextAscii(
        title = "Item Name",
        help = "Help for the item"
    ),
    #Match
    match_type = "first"
)

Wir haben als default Parameter ein Tuple gespeichert mit 2 Werten, Warn und Crit. Dementsprechend muss unsere WATO Regel ebenfalls ein Tuple mit 2 Werten erzeugen.

Die Funktion register_check_parameters() erzeugt eine neue möglichkeit Regeln zu definieren.

Die Funktion erwartet 6 Parameter:

1 Kategorie – Die Kategorie ist die Rubrik unter der die neue Regel angezeigt wird.

2 Group – Die Gruppe macht die Zuordnung zum Check. Im Check im check_info gibt es hierfür im Dictionary den Key „group“. (siehe oben im Beispiel)

3 Rulename – Der Rulename wird in der Übersicht angezeigt

4 Parameter – Hier werden die Parameter definiert.

5 Item – Es gibt hier 2 Möglichkeiten:

None – Die Regel kann nur für den Host definiert werden und gilt für alle Services die auf die Group matchen.

TextAscii( title = „Item Name“, help = „Help for the item“ ) –  Hier kann ein Servicename definiert werden, die Regel gilt dann nur für einen bestimmten Servicenamen

6 Matchtype – Es gibt 2 Matchtypen:

first – Die erste Regel die Zutrifft wird angewendet.

dict – Eine Kombination aus mehreren Werten ist möglich aus mehreren Regeln. Der Typ ist zwingend wenn der äußere Parametertyp Dictionary ist.

Der Parameter Block

#Parameter
    Tuple(
        title = "Printer pages max value",
        help = "Define critical and warning values",
        elements = [
            Integer(title="Warn at", unit="pages"),
            Integer(title="Critical at", unit="pages"),
        ]
    ),

Das äußere Tuple() ist eine von Check_MK bereitgestellte Funktion, es gibt noch viele weitere (z.B. TextAscii, Integer, Float, ListOf, Dictionary, etc.) Weitere Beispiele findet ihr direkt im Check_MK Code unter ~/share/check_mk/web/plugins/wato/.

Das Tuple() hat in diesem Fall 3 Übergabewerte:

title – Gibt den Titel der Werte an

help – Wenn die Hilfe aktiviert ist (Buch rechts oben in der Ecke) werden die Hilfstexte angezeigt

elements – Sind die Werte die in WATO eingegeben werden können, diese können beliebig oft, entsprechend dem Datenformat, verschachtelt werden. In elements befinden sich unsere 2 Integer() Werte Warn und Crit.

Zu den WATO Erweiterungen wird noch ein gesonderter Artikel folgen den ich hier verlinken werde.

Fertig ??

Jetzt können wir den Check mal Testen, dazu in WATO ein „Tabula Rasa“ für den Host machen, der neue Check sollte dann auftauchen.

Viel Spaß beim Spielen 🙂

 

Check_MK: Helferlein und Treasures

Check_MK hat ein paar kleine Helferlein & Treasures an Board die nicht jeder kennt.

mkcheck

Mkcheck ist ein Script das ein Template für einen neuen eigenen Check erzeugt.

Zu finden ist es unter ~/share/doc/check_mk/helpers/mkcheck

OMD[test1]:~$ ~/share/doc/check_mk/helpers/mkcheck -h

DESCRIPTION:
  This script creates a basic check file or if '-m'
  specified a manual page.
  You have to specify at least one name.

  The created check or manual page are saved to
  the current directory.

  It is recommended to run this script in the checks folder
  if you want to create a check or in the manual page folder
  if you want to create a man page.

USAGE: mkcheck [OPTS] ARGS

OPTIONS:
 -h, --help        Show this help
 -s                Adds SNMP info and scan function
 -p                Adds parse function
 -m                Creates the manual page instead of
                   the check

Beispiel für einen SNMP Check:

OMD[test1]:~$ ~/share/doc/check_mk/helpers/mkcheck -sp my_snmp_check
OMD[test1]:~$ cat my_snmp_check 

#!/usr/bin/python
# -*- encoding: utf-8; py-indent-offset: 4 -*-
# +------------------------------------------------------------------+
# |             ____ _               _        __  __ _  __           |
# |            / ___| |__   ___  ___| | __   |  \/  | |/ /           |
# |           | |   | '_ \ / _ \/ __| |/ /   | |\/| | ' /            |
# |           | |___| | | |  __/ (__|   <    | |  | | . \            |
# |            \____|_| |_|\___|\___|_|\_\___|_|  |_|_|\_\           |
# |                                                                  |
# | Copyright Mathias Kettner 2017             mk@mathias-kettner.de |
# +------------------------------------------------------------------+
#
# This file is part of Check_MK.
# The official homepage is at http://mathias-kettner.de/check_mk.
#
# check_mk is free software;  you can redistribute it and/or modify it
# under the  terms of the  GNU General Public License  as published by
# the Free Software Foundation in version 2.  check_mk is  distributed
# in the hope that it will be useful, but WITHOUT ANY WARRANTY;  with-
# out even the implied warranty of  MERCHANTABILITY  or  FITNESS FOR A
# PARTICULAR PURPOSE. See the  GNU General Public License for more de-
# tails. You should have  received  a copy of the  GNU  General Public
# License along with GNU Make; see the file  COPYING.  If  not,  write
# to the Free Software Foundation, Inc., 51 Franklin St,  Fifth Floor,
# Boston, MA 02110-1301 USA.


# example output


def parse_my_snmp_check(info):
    pprint.pprint(info)
    parsed = {}
    return parsed


def inventory_my_snmp_check(parsed):
    return []


def check_my_snmp_check(item, params, parsed):
    return 3, 'not yet implemented'


check_info['my_snmp_check'] = {
    'parse_function'        : parse_my_snmp_check,
    'inventory_function'    : inventory_my_snmp_check,
    'check_function'        : check_my_snmp_check,
    'service_description'   : 'DESCR',
    'snmp_info'             : ('', []),
    'snmp_scan_function'    : lambda oid: False,
}

figheader

Figheader ist ein Helper der Kommentarblöcke im Check_MK Style erzeugt. figlet muss auf dem System installiert sein.

Zu finden unter ~/share/doc/check_mk/helpers/figheader

OMD[test1]:~$ ~/share/doc/check_mk/helpers/figheader whoohoo
#.
#   .--whoohoo-------------------------------------------------------------.
#   |                     _                 _                              |
#   |           __      _| |__   ___   ___ | |__   ___   ___               |
#   |           \ \ /\ / / '_ \ / _ \ / _ \| '_ \ / _ \ / _ \              |
#   |            \ V  V /| | | | (_) | (_) | | | | (_) | (_) |             |
#   |             \_/\_/ |_| |_|\___/ \___/|_| |_|\___/ \___/              |
#   |                                                                      |
#   +----------------------------------------------------------------------+
#   |                                                                      |
#   '----------------------------------------------------------------------'

wato_import.py

Wato_import.py ist ein Script aus der Schatzkiste und dient zum Import einer CSV Liste in WATO.

Zu finden unter ~/share/doc/check_mk/treasures/wato_import.py

OMD[test1]:~/share/doc/check_mk/treasures$ python wato_import.py 
Run this script inside a OMD site
    Usage: ./wato_import.py csvfile.csv
    CSV Example:
    wato_foldername;hostname|tag1 tag2;host_alias;ipaddress|None

cmk-delete-host

Cmk-delete-host ist ebenfalls aus der Schatzkiste und dient zum Löschen von Hosts von der Kommandozeile aus. Dazu wird die Automation Schnittstelle verwendet.

Zu finden unter ~/share/doc/check_mk/treasures/cmk-delete-host

extract_inventory.py

Extract_inventory.py ist aus der Schatzkiste und exportiert die Inventory Daten als CSV.

Zu finden unter ~/share/doc/check_mk/treasures/inventory/extract_inventory.py

OMD[test1]:~/share/doc/check_mk/treasures/inventory$ ./extract_inventory.py 
creating relation inv_raw_generic(OS) for localhost
creating relation inv_raw_generic(Linux) for localhost
creating relation inv_raw_arp for localhost
creating relation devices for localhost
creating relation inv_raw_file for localhost

mmm@max:/var/tmp# ls -la
insgesamt 32
drwxrwxrwt  3 root  root  4096 Jul 14 11:41 .
drwxr-xr-x 13 root  root  4096 Feb  3  2014 ..
-rw-r--r--  1 test1 test1  377 Jul 14 11:41 devices
-rw-r--r--  1 test1 test1   75 Jul 14 11:41 inv_raw_arp
-rw-r--r--  1 test1 test1  114 Jul 14 11:41 inv_raw_file
-rw-r--r--  1 test1 test1   59 Jul 14 11:41 inv_raw_generic(Linux)
-rw-r--r--  1 test1 test1   43 Jul 14 11:41 inv_raw_generic(OS)

mmm@max:/var/tmp# cat devices 
"import_id", "import_data_source_id", "import_org_level_2_id", "device_key", "device_manufacturer", "device_model", "serial_number", "operating_system", "inventory_date", "installation_date", "cpu_socket_count", "cpu_chip_count", "cpu_core_count", "cpu_speed", "cpu_name"
"localhost", "sla", "default", "localhost", "", "", "", "", "2017-07-14", "", "", "1", "4", "3200.0", ""

Inoffizielle Notification Scripts

In den Treasures befinden sich noch einige Notification Scripts welche man gut als Vorlage für eigene Scripte verwenden kann. Eigene Notification Scripte sind unter ~/local/share/check_mk/notifications abzulegen.

OMD[test1]:~/share/doc/check_mk/treasures/notifications$ ls -la
insgesamt 64
drwxr-xr-x  2 root root  4096 Jul 11 13:55 ./
drwxr-xr-x 21 root root  4096 Jul 11 13:55 ../
-rwxr-xr-x  1 root root  2829 Okt 12  2016 braintower*
-rwxr-xr-x  1 root root 24497 Jul  5 14:53 glpi.py*
-rwxr-xr-x  1 root root  3842 Okt 12  2016 mobilant*
-rwxr-xr-x  1 root root  5114 Okt 12  2016 multitech*
-rwxr-xr-x  1 root root  2059 Okt 12  2016 opcmsg*
-rw-r--r--  1 root root   344 Okt 12  2016 README
-rwxr-xr-x  1 root root   632 Okt 12  2016 slack*
-rwxr-xr-x  1 root root   803 Okt 12  2016 snmp_trap*

Andere Dinge

In der Schatzkiste und in vielen Ordnern im Check_MK Root verbergen sich noch viele coole Sachen am besten geht ihr mal selbst auf die Suche 🙂

~/share/doc/check_mk/treasures

~/share/doc/check_mk/helpers

Check_MK: Eigener Agent Check

Neben den lokalen Checks gibt es noch Checks die als Plugin im Agenten laufen. Der Unterschied liegt darin das auf dem Host auf dem der Agent läuft keine Bewertung der Informationen stattfindet, diese werden dem Check_MK Monitoring nur als Text zur Verfügung gestellt. Somit basiert ein Agent Plugin aus einem Host Teil (Agent Plugin) und einem Monitoring Server Teil (Check_MK Check).

Alle Dateien des DEMOs als MKP zum Download:

 

Das Agent Plugin

Hierzu ein Beispiel:

Ein Check der die aktuellen offen TCP Listener Ports ausgibt. Die Scriptsprache ist egal, die Datei muss executeable sein.

my_tcp_listener.sh Script in /usr/lib/check_mk_agent/plugins

#!/bin/bash

echo "<<<my_tcp_listener>>>"

netstat -tlpen | sed 1,2d

Im <<<>>> Block wird der Checkname angegeben.

Folgende Ausgabe erzeugt das Plugin:

mmm@home:/home/mmm$ /usr/lib/check_mk_agent/plugins/test.sh
<<<my_tcp_listener>>>
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      0          12608       621/sshd        
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      0          14982       1034/exim4      
tcp        0      0 0.0.0.0:6556            0.0.0.0:*               LISTEN      0          14882       793/xinetd      
tcp        0      0 127.0.0.1:6942          0.0.0.0:*               LISTEN      1000       869419      15640/java      
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      999        13258       1230/apache2    
tcp        0      0 127.0.0.1:63342         0.0.0.0:*               LISTEN      1000       867186      15640/java      
tcp        0      0 0.0.0.0:34607           0.0.0.0:*               LISTEN      105        1941        599/rpc.statd   
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN      0          1882        590/rpcbind     
tcp6       0      0 :::22                   :::*                    LISTEN      0          12610       621/sshd        
tcp6       0      0 :::39352                :::*                    LISTEN      105        1945        599/rpc.statd   
tcp6       0      0 ::1:25                  :::*                    LISTEN      0          14983       1034/exim4      
tcp6       0      0 :::111                  :::*                    LISTEN      0          1885        590/rpcbind     
tcp6       0      0 :::80                   :::*                    LISTEN      0          13641       1006/apache2    

Die Ausgabe wird wenn der Agent vom Server abgefragt wird angehängt. Das kann man selbst testen mit einem „telnet <host> 6556“.

In der Check Instanz auf dem Server muss nun noch ein Check geschrieben werden der die Ausgabe des Agenten verarbeiten kann.

Die eigenen Check Plugins auf dem Monitoring Server liegen unter /omd/sites/<sitename>/local/share/check_mk/checks

Beispiel zu my_tcp_listener, die Scriptsprache ist hier zwingend Python:

#!/usr/bin/python

def inventory_my_tcp_listener(info):
  for l in info:
    service_identify = "%s/%s" % (l[0], l[3].split(":")[-1])
    yield service_identify, None

def check_my_tcp_listener(item, params, info):

  proto, port = item.split("/")
  message = (2, "CRIT service not listen")

  for l in info:
    if proto in l[0] and port in l[3].split(":")[-1]:
      message = (0, "OK service listen")

  return message

check_info["my_tcp_listener"] = {
  "inventory_function" :	inventory_my_tcp_listener,
  "check_function" :	check_my_tcp_listener,
  "service_description" : "TCP Listener %s",
}

Die Bestandteile des Check_MK Server Plugins

Inventory Funktion

Die Inventory Funktion dient dazu das WATO neue Services automatisch anlegen kann. Der info Variable wird die Ausgabe des Host Plugins übergeben. Dabei wird jede Zeile in eine Python Liste umgewandelt.

Eine Debugging Ausgabe ist möglich in dem man in die Inventory Funktion direkt ein print info einbaut. Check_MK stellt auch Pretty Print aus Python bereit, das Modul muss nicht importiert werden.

print info:

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost
Discovering services on localhost:
localhost:
[[u'tcp', u'0', u'0', u'0.0.0.0:22', u'0.0.0.0:*', u'LISTEN', u'0', u'12608', u'621/sshd'], [u'tcp', u'0', u'0', u'127.0.0.1:25', u'0.0.0.0:*', u'LISTEN', u'0', u'14982', u'1034/exim4'], [u'tcp', u'0', u'0', u'0.0.0.0:6556', u'0.0.0.0:*', u'LISTEN', u'0', u'14882', u'793/xinetd'], [u'tcp', u'0', u'0', u'127.0.0.1:6942', u'0.0.0.0:*', u'LISTEN', u'1000', u'869419', u'15640/java'], [u'tcp', u'0', u'0', u'127.0.0.1:5000', u'0.0.0.0:*', u'LISTEN', u'999', u'13258', u'1230/apache2'], [u'tcp', u'0', u'0', u'127.0.0.1:63342', u'0.0.0.0:*', u'LISTEN', u'1000', u'867186', u'15640/java'], [u'tcp', u'0', u'0', u'0.0.0.0:34607', u'0.0.0.0:*', u'LISTEN', u'105', u'1941', u'599/rpc.statd'], [u'tcp', u'0', u'0', u'0.0.0.0:111', u'0.0.0.0:*', u'LISTEN', u'0', u'1882', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::22', u':::*', u'LISTEN', u'0', u'12610', u'621/sshd'], [u'tcp6', u'0', u'0', u':::39352', u':::*', u'LISTEN', u'105', u'1945', u'599/rpc.statd'], [u'tcp6', u'0', u'0', u'::1:25', u':::*', u'LISTEN', u'0', u'14983', u'1034/exim4'], [u'tcp6', u'0', u'0', u':::111', u':::*', u'LISTEN', u'0', u'1885', u'590/rpcbind'], [u'tcp6', u'0', u'0', u':::80', u':::*', u'LISTEN', u'0', u'13641', u'1006/apache2']]

oder mit pprint.pprint(info)

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost
Discovering services on localhost:
localhost:
[[u'tcp',
  u'0',
  u'0',
  u'0.0.0.0:22',
  u'0.0.0.0:*',
  u'LISTEN',
  u'0',
  u'12608',
  u'621/sshd'],
 [u'tcp',
  u'0',
  u'0',
  u'127.0.0.1:25',
  u'0.0.0.0:*',
  u'LISTEN',
  u'0',
  u'14982',
  u'1034/exim4'],
 [u'tcp',
  u'0',
  u'0',
  u'0.0.0.0:6556',
  u'0.0.0.0:*',
  u'LISTEN',
  u'0',
  u'14882',
  u'793/xinetd'],
 [u'tcp',
  u'0',
  u'0',
  u'127.0.0.1:6942',
  u'0.0.0.0:*',
  u'LISTEN',
  u'1000',
  u'869419',
  u'15640/java'],
 [u'tcp',
  u'0',
  u'0',
  u'127.0.0.1:5000',
  u'0.0.0.0:*',
  u'LISTEN',
  u'999',
  u'13258',
  u'1230/apache2'],
 [u'tcp',
  u'0',
  u'0',
  u'127.0.0.1:63342',
  u'0.0.0.0:*',
  u'LISTEN',
  u'1000',
  u'867186',
  u'15640/java'],
 [u'tcp',
  u'0',
  u'0',
  u'0.0.0.0:34607',
  u'0.0.0.0:*',
  u'LISTEN',
  u'105',
  u'1941',
  u'599/rpc.statd'],
 [u'tcp',
  u'0',
  u'0',
  u'0.0.0.0:111',
  u'0.0.0.0:*',
  u'LISTEN',
  u'0',
  u'1882',
  u'590/rpcbind'],
 [u'tcp6',
  u'0',
  u'0',
  u':::22',
  u':::*',
  u'LISTEN',
  u'0',
  u'12610',
  u'621/sshd'],
 [u'tcp6',
  u'0',
  u'0',
  u':::39352',
  u':::*',
  u'LISTEN',
  u'105',
  u'1945',
  u'599/rpc.statd'],
 [u'tcp6',
  u'0',
  u'0',
  u'::1:25',
  u':::*',
  u'LISTEN',
  u'0',
  u'14983',
  u'1034/exim4'],
 [u'tcp6',
  u'0',
  u'0',
  u':::111',
  u':::*',
  u'LISTEN',
  u'0',
  u'1885',
  u'590/rpcbind'],
 [u'tcp6',
  u'0',
  u'0',
  u':::80',
  u':::*',
  u'LISTEN',
  u'0',
  u'13641',
  u'1006/apache2']]

Im Beispiel wird jetzt jede Zeile durchgegangen und ein eindeutiger Identifier (Servicename) erzeugt für den Service.

for l in info:
    service_identify = "%s/%s" % (l[0], l[3].split(":")[-1])
    yield service_identify, None

Die Rückgabe der Funktion erfolgt über die Generator Funktion yield und erwartet ein Tuple aus 2 Werten. 1 Wert ist der Servicename, 2 Wert sind Parameter die als Tuple oder Dictionary übergeben werden können. In dem Beispiel haben wir keine Parameter somit übergeben wir ein None.

Hat man seine Inventory Funktion und die check_info Definition in seinem Check komplettiert kann man das auf der Kommandozeile testen:

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II localhost
Discovering services on localhost:
localhost:
    1 cpu.loads
    1 cpu.threads
    1 df
    1 diskstat
    3 kernel
    1 kernel.util
    1 livestatus_status
    1 lnx_if
    3 local
    1 mem.linux
    1 mkeventd_status
    1 mknotifyd
    1 mounts
    3 mtr
   13 my_tcp_listener
    1 ntp.time
    1 omd_apache
    1 omd_status
    1 tcp_conn_stats
    1 uptime

Die Inventory Funktion hat meine 13 lokalen TCP Ports gefunden und dem Host hinzugefügt. Gespeichert werden diese Informationen an folgendem Ort:

/omd/sites/<sitename>/var/check_mk/autochecks/<hostname>.mk

OMD[test1]:~/var/check_mk/autochecks$ cat localhost.mk 
[
  ('cpu.loads', None, cpuload_default_levels),
  ('cpu.threads', None, threads_default_levels),
  ('df', u'/', {}),
  ('diskstat', u'SUMMARY', diskstat_default_levels),
  ('kernel', u'Context Switches', kernel_default_levels),
  ('kernel', u'Major Page Faults', kernel_default_levels),
  ('kernel', u'Process Creations', kernel_default_levels),
  ('kernel.util', None, {}),
  ('livestatus_status', u'test1', {}),
  ('lnx_if', u'2', {'state': ['1'], 'speed': 1000000000}),
  ('mem.linux', None, {}),
  ('mkeventd_status', u'test1', {}),
  ('mknotifyd', u'test1', {}),
  ('mounts', u'/', [u'data=ordered', u'errors=remount-ro', u'relatime', u'rw']),
  ('my_tcp_listener', u'tcp/111', None),
  ('my_tcp_listener', u'tcp/22', None),
  ('my_tcp_listener', u'tcp/25', None),
  ('my_tcp_listener', u'tcp/34607', None),
  ('my_tcp_listener', u'tcp/5000', None),
  ('my_tcp_listener', u'tcp/63342', None),
  ('my_tcp_listener', u'tcp/6556', None),
  ('my_tcp_listener', u'tcp/6942', None),
  ('my_tcp_listener', u'tcp6/111', None),
  ('my_tcp_listener', u'tcp6/22', None),
  ('my_tcp_listener', u'tcp6/25', None),
  ('my_tcp_listener', u'tcp6/39352', None),
  ('my_tcp_listener', u'tcp6/80', None),
  ('ntp.time', None, {}),
  ('omd_apache', u'test1', None),
  ('omd_status', u'test1', None),
  ('tcp_conn_stats', None, tcp_conn_stats_default_levels),
  ('uptime', None, {}),
]

Check Funktion

Die Check Funktion beinhaltet die Logik welche überprüft in welchem Zustand sich ein Service befindet (0=OK, 1=WARNING, 2=CRITICAL, 3=UNKNOWN).

Der Check Funktion werden 3 Variablen übergeben: item, params, info

Variable item

Der Variable wird der Servicename übergeben

Variable params

Hier werden die Parameter übergeben die beim Inventory oder durch eine WATO Regel für den Service gesetzt sind.

Variable info

Hier wird die komplette Ausgabe des Agent Check Plugins als Liste übergeben so wie bei der Inventory Funktion.

Die Check Funktion ist hier stark vereinfacht, grundsätzlich muss man sein item aus der Datenmenge info herraussuchen:

Beispiel aus einem anderen Check:

for line in info:
    if item == line[0]:
        # Hier der Check

Hier für unser Beispiel:

def check_my_tcp_listener(item, params, info):

    proto, port = item.split("/")
    message = (2, "CRIT service not listen")

    for l in info:
        if proto in l[0] and port in l[3].split(":")[-1]:
            message = (0, "OK service listen")

    return message

Als Rückgabe erwartet Check_MK aus 2 bzw. 3 Werten in einem Tuple. 2 Werte wenn keine Performance Daten übergeben werden, 3 wenn Performance Daten dabei sind.

ohne Performance Werte

<STATUS>, <MESSAGE>

mit Performance Werte

<STATUS>, <MESSAGE>, <PERFDATA>

Die Perfomance Werte sind wiederum eine Liste aus Tupeln. Das werde ich in einem anderen Artikel erläutern.

Das check_info Dictionary

Das check_info Dictionary ist ein Verzeichnis das von Check_MK selbst bereitgestellt wird und alle zur Verfügung stehenden Checks beinhaltet.

check_info["my_tcp_listener"] = {
    "inventory_function" :  inventory_my_tcp_listener,
    "check_function" :  check_my_tcp_listener,
    "service_description" : "TCP Listener %s",
}

Der Key muss dem Namen entsprechen welche der Agent für die Sektion z.B. <<<my_tcp_listener>>> verwendet. Im Dictionary werden dann Check_MK die Inventory Funktion und die Check Funktion mitgeteilt. Desweiteren kann man hier den Text definieren welcher für den Service angezeigt wird. Hier wird z.B. für SNMP Checks die OIDs definiert. Mehr dazu in einem anderen Artikel …

Wenn der Check fertig ist muss der Check_MK Core neu geladen werden damit er den Check kennt, das erledigt folgendes Kommando in der OMD Umgebung „cmk -R“

Ob der Check funktioniert kann man mit „cmk -D localhost“ in der OMD Umgebung testen.

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -D localhost

localhost                                                                      
Addresses:              127.0.0.1
Tags:                   /wato/, cmk-agent, ip-v4, ip-v4-only, lan, prod, site:test1, tcp, wato
Host groups:            
Contact groups:         all
Type of agent:          TCP (port: 6556)
Is aggregated:          no
Services:
  checktype         item                           params                                                                                                                                                                                                                                                                                                                                                   description                    groups summarized to groups
  ----------------- ------------------------------ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------ ------ ------------- ------
...
  my_tcp_listener   tcp/111                        None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/111                                      
  my_tcp_listener   tcp/22                         None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/22                                       
  my_tcp_listener   tcp/25                         None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/25                                       
  my_tcp_listener   tcp/34607                      None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/34607                                    
  my_tcp_listener   tcp/5000                       None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/5000                                     
  my_tcp_listener   tcp/63342                      None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/63342                                    
  my_tcp_listener   tcp/6556                       None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/6556                                     
  my_tcp_listener   tcp/6942                       None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp/6942                                     
  my_tcp_listener   tcp6/111                       None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp6/111                                     
  my_tcp_listener   tcp6/22                        None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp6/22                                      
  my_tcp_listener   tcp6/25                        None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp6/25                                      
  my_tcp_listener   tcp6/39352                     None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp6/39352                                   
  my_tcp_listener   tcp6/80                        None                                                                                                                                                                                                                                                                                                                                                     TCP Listener tcp6/80                                      
....

OMD[test1]:~/local/share/check_mk/checks$ 

Debugging Plugin

Per default werden Exceptions auf der Kommandozeile unterdrückt. Man kann mit –debug die Ausgaben beim „cmk“ Kommando einschalten.

OMD[test1]:~/local/share/check_mk/checks$ cmk -v -II --debug localhost
Discovering services on localhost:
localhost:
Traceback (most recent call last):
  File "/omd/sites/test1/share/check_mk/modules/check_mk.py", line 4838, in <module>
    do_discovery(hostnames, check_types, seen_I == 1)
  File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 80, in do_discovery
    do_discovery_for(hostname, check_types, only_new, use_caches, on_error)
  File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 100, in do_discovery_for
    new_items = discover_services(hostname, check_types, use_caches, do_snmp_scan, on_error)
  File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 718, in discover_services
    return services + discover_services_impl(hostname, check_types, use_caches, on_error, ipaddress)
  File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 733, in discover_services_impl
    use_caches, on_error, use_snmp):
  File "/omd/sites/test1/share/check_mk/modules/discovery.py", line 899, in discover_check_type
    discovered_items = list(discovered_items)
  File "/omd/sites/test1/local/share/check_mk/checks/my_tcp_listener", line 6, in inventory_my_tcp_listener
    yield service_identify, None, xxxx,xxxx,xx
NameError: global name 'xxxx' is not defined

Fertig ???

Im WATO muss man seinen neuen Check auf dem Host noch mit einem einfachen „Tabula Rasa“ inventarisieren.

Jetzt kann man sein Ergebnis auf der Webseite bewundern 🙂

 

Check_MK: Einfacher lokaler Check (Agent)

Dem Check_MK Agenten ist es egal mit welcher Sprache ein lokaler Check geschrieben ist, er muss vom Betriebsystem ausführbar sein. Somit kann jede Scriptsprache (Perl, PHP, Python, Powershell, VBS, etc.) verwendet werden. Der Agent erwartet folgendes Format:

<checkresult> <name> <perfdata> <status_text>

Checkresult

0 = OK – 1 = Warning – 2 = Crtitcal – 3 = Unknown

Name

Dies Repräsentiert den Namen des Checks, whitespaces sind durch Unterstriche zu ersetzen, z.B. DNS_Check

Perfdata

Wenn von dem Check keine Performancedaten erzeugt werden ist hier ein Minus – einzusetzen; Die Werte sind in folgendem Format zu übergeben:

varname=value;warn;crit;min;max

Min und Max sind nur für das alte PNP4Nagios bei der freien Check_MK Version, das neue Graphing System der Enterprise Version benötigt das nicht und können weggelassen werden.

Mehrere Performancewerte werden durch ein Pipe | getrennt. z.B.

varname=value;warn;crit|varname2=value;warn;crit|varname3=value;warn;crit

Checks z.B. nur alle 10 Minuten ausführen

Per default wird der lokale Check jedes mal ausgeführt wenn Check_MK den Agenten abfrägt, das Verhalten kann man manipulieren in dem man im local Ordner einen Ordner anlegt in Sekunden, z.B. 10 Minuten sind 600 Sekunden. Der Check mein_check.sh wird dann im Ordner local/600/mein_check.sh abgelegt. Das Checkergebnis wird für die Anzahl der Sekunden des Ordners gecached.

Die Local Checks liegen in einem Linux System unter /usr/lib/check_mk_agent/local, in Windows ist es abhänig vom Installationsort. Diesen kann man ermitteln indem man den Agenten per Telnet abfrägt:

telnet 1.2.3.4 6556
<<<check_mk>>>
...
LocalDirectory: C:\program files\check_mk_agent\local
...

Bei Windows Maschinen muss noch in der check_mk.ini definiert werden welche Scriptendungen ausgeführt werden dürfen.

# Example configuration for Windows agent
[global]
    # Restrict access to certain IP addresses
    # If ipv6 is enabled, all listed ipv4 adresses are also accepted as
    # source adresses in their ipv6-mapped form. I.e. if
    # 192.168.56.0/24 is listed, connections from ::ffff:c0a8:3800/120
    # are also possible
    # only_from = 127.0.0.1 192.168.56.0/24 ::1

    # Change port where the agent is listening ( default 6556 )
    # port = 6556

    # Disable ipv6 support. By default, ipv4 is always supported
    # and ipv6 is enabled additionally if supported by the system.
    # ipv6 = no

    # Do only execute programs and scripts with
    # the following suffixes (in local and plugins).
    # Per default all extensions except txt and dir
    # are being executed.
    execute = exe bat vbs ps1

Zu finden ist ein Beispiel INI File unter /omd/versions/default/share/check_mk/agents/windows/check_mk.example.ini

Einfacher lokaler Check als Python Script:

#!/usr/bin/env python

import os

warn = 100000
crit = 200000

def size(path = '.'):
    tsize = 0
    for dp, dn, fn in os.walk(path):
        for f in fn:
            fp = os.path.join(dp, f)
            tsize += os.path.getsize(fp)
    return tsize

tmp_size = size("/tmp")

if tmp_size >= crit:
    print "2 tmp dir size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)
elif tmp_size >= warn:
    print "1 tmp_dir_size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)
else:
    print "0 tmp_dir_size size=%d;%d;%d TMP size is %d " % (tmp_size, tmp_size, warn, crit)



Ein weiteres tolles Feature der lokalen Checks ist das man sich theoretisch nicht um die Logik kümmern muss damit OK, WARN oder CRIT zurückgegeben wird. Dazu müssen bei dem Counter Warning und Critical Werte mitgegeben werden. Dazu gibt es ein schönes Beispiel in den „Treasures“ von Check_MK (siehe localchecks/zombies).

Die Treasures sind zu finden unter: /omd/versions/default/share/doc/check_mk/treasures

Hier ein Beispiel:

#!/bin/bash

proc=$( ps aux | wc -l )

echo "P Processes processes=$proc;1000;5000"

Mit P am Anfang anstatt 0,1,2,3 wird der Status von Check_MK bestimmt.

Andere Beispiele, etc. bei Mathias Kettner direkt und weitere Infos: (siehe z.B. filecount) https://mathias-kettner.de/checkmk_localchecks.html

Enterasys Logging & Debugging

Kürzlich wurde ich von einem Kunden mit einem Problem konfrontiert das im Enterasys NetSight im Traplog immer wieder „Incorrect Community Name“ auftaucht. SNMP war im NetSight korrekt konfiguriert. Der Kunde wollte wissen wer versucht auf dem Switch zuzugreifen per SNMP. In der Trap Meldung wird diese Information leider nicht mitgesendet. Die bekannten Debug Befehle die man von Cisco kennt gibt es nicht aber man kann den Enterasysgeräten auch einige Informationen entlocken wenn man weiß wo man suchen muss.

Man kann bei Enterasys das Loggingverhalten für verschiedene Systemdienste anpassen um mehr Informationen zu bekommen.

Mit dem Kommando „show logging application“ oder kurz „sh logg app“ angezeigt werden.

Hier eine Liste von den verschiedenen Switch Serien und Applications wo man das Logging ändern kann:
Enterasys B3 Serie:

         Application   Current Severity Level
---------------------------------------------
 89      CLIWEB                   6
 90      SNMP                     7
 91      STP                      6
 92      Driver                   6
 93      System                   7
 94      Stacking                 6
 112     UPN                      6
 118     Router                   6

1(emergencies)  2(alerts)       3(critical)
4(errors)       5(warnings)     6(notifications)
7(information)  8(debugging)

Enterasys C2 Serie:

         Application   Current Severity Level
---------------------------------------------
 89      CLIWEB                   6
 90      SNMP                     6
 91      STP                      6
 92      Driver                   6
 93      System                   6
 94      Stacking                 6
 95      RtrOspf                  6
 96      RtrMcast                 6
 97      RtrVrrp                  6
 112     UPN                      6
 118     Router                   6

1(emergencies)  2(alerts)       3(critical)
4(errors)       5(warnings)     6(notifications)
7(information)  8(debugging)

Enterasys N Serie:

        Application   Current Severity Level Server List
----------------------------------------------------------
  88    RtrAcl                           6      1-8,console,file
  89    CLI                              6      1-8,console,file
  90    SNMP                             6      1-8,console,file
  91    Webview                          6      1-8,console,file
  93    System                           6      1-8,console,file
  95    RtrFe                            6      1-8,console,file
  96    Trace                            6      1-8,console,file
 105    RtrLSNat                         6      1-8,console,file
 111    FlowLimt                         6      1-8,console,file
 112    UPN                              6      1-8,console,file
 117    AAA                              6      1-8,console,file
 118    Router                           6      1-8,console,file
 140    AddrNtfy                         6      1-8,console,file
 141    OSPF                             6      1-8,console,file
 142    VRRP                             6      1-8,console,file
 145    RtrArpProc                       6      1-8,console,file
 147    LACP                             6      1-8,console,file
 148    RtrNat                           6      1-8,console,file
 151    RtrTwcb                          6      1-8,console,file
 154    DbgIpPkt                         6      1-8,console,file
 158    HostDoS                          6      1-8,console,file
 180    RtrMcast                         6      1-8,console,file
 183    PIM                              6      1-8,console,file
 184    DVMRP                            6      1-8,console,file
 185    BGP                              6      1-8,console,file
 196    LinkFlap                         6      1-8,console,file
 199    Spoof                            6      1-8,console,file
 207    IPmcast                          6      1-8,console,file
 209    Spantree                         6      1-8,console,file
 211    trackobj                         6      1-8,console,file
 213    LinkTrap                         6      1-8,console,file
 214    CDP                              6      1-8,console,file
 215    LLDP                             6      1-8,console,file
 216    CiscoDP                          6      1-8,console,file
 222    Security                         6      1-8,console,file
 225    RMON                             6      1-8,console,file
 231    IPsec                            6      1-8,console,file

1(emergencies)  2(alerts)       3(critical)
4(errors)       5(warnings)     6(notifications)
7(information)  8(debugging)

Enterasys S Serie SSA (SW-release mit VSB = Virtual Chassis Bonding):

        Application   Current Severity Level Server List
----------------------------------------------------------
  88    RtrAcl                           6      1-8,console,file
  89    CLI                              6      1-8,console,file
  90    SNMP                             6      1-8,console,file
  91    Webview                          6      1-8,console,file
  93    System                           6      1-8,console,file
  95    RtrFe                            6      1-8,console,file
  96    Trace                            6      1-8,console,file
 105    RtrLSNat                         6      1-8,console,file
 111    FlowLimt                         6      1-8,console,file
 112    UPN                              6      1-8,console,file
 117    AAA                              6      1-8,console,file
 118    Router                           6      1-8,console,file
 140    AddrNtfy                         6      1-8,console,file
 141    OSPF                             6      1-8,console,file
 142    VRRP                             6      1-8,console,file
 145    RtrArpProc                       6      1-8,console,file
 147    LACP                             6      1-8,console,file
 148    RtrNat                           6      1-8,console,file
 151    RtrTwcb                          6      1-8,console,file
 154    DbgIpPkt                         6      1-8,console,file
 158    HostDoS                          6      1-8,console,file
 180    RtrMcast                         6      1-8,console,file
 183    PIM                              6      1-8,console,file
 184    DVMRP                            6      1-8,console,file
 185    BGP                              6      1-8,console,file
 196    LinkFlap                         6      1-8,console,file
 199    Spoof                            6      1-8,console,file
 207    IPmcast                          6      1-8,console,file
 209    Spantree                         6      1-8,console,file
 211    trackobj                         6      1-8,console,file
 213    LinkTrap                         6      1-8,console,file
 214    CDP                              6      1-8,console,file
 215    LLDP                             6      1-8,console,file
 216    CiscoDP                          6      1-8,console,file
 218    OAM                              6      1-8,console,file
 222    Security                         6      1-8,console,file
 225    RMON                             6      1-8,console,file
 231    IPsec                            6      1-8,console,file
 239    Bonding                          6      1-8,console,file
 242    HAUpgrade                        6      1-8,console,file

1(emergencies)  2(alerts)       3(critical)
4(errors)       5(warnings)     6(notifications)
7(information)  8(debugging)

Um jetzt mehr sehen zu können muss der Severity Level angepasst werden für die Application die man „debuggen“ will. Ein meinem Fall war es SNMP auf einem B3 Switch also => SNMP.

 
set logging application SNMP level 7

Wer die Informationen an einen Syslog Server senden will sollte noch einen Syslogserver hinterlegen mit den richtigen Severity Level.

 
set logging server 1 ip-addr 10.1.1.1 severity 8 description "default" state enable

Alternativ kann man sich die Syslogs auch auf der Console ausgeben lassen:

A/B/C/D/I-Serie auf Console:

 
set logging local console enable

A/B/C/D/I/N/S-Serie in File:

 
set logging local console disable file enable

N/S-Serie:

 
set logging here enable

Im Syslog tauchte dann auch das System auf das versucht hat mit einer falschen SNMP Community auf die Switche zuzugreifen. Es war ein Printer Verwaltungserver (HP Jetadmin) der die komplette IP-Range des Standorts scannt und versucht per SNMP Parameter von den Geräten abzufragen (Tonerstand, etc.)

 
<166>Feb 1 10:33:06 192.168.1.52-1 TRAPMGR[175072816]: traputil.c(475) 260 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.2-1 TRAPMGR[138984840]: traputil.c(475) 430 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.4-1 TRAPMGR[138984160]: traputil.c(475) 245 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.8-1 TRAPMGR[138983208]: traputil.c(475) 264 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.11-1 TRAPMGR[138983560]: traputil.c(475) 327 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.14-2 TRAPMGR[138984192]: traputil.c(475) 203 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.15-1 TRAPMGR[138983728]: traputil.c(475) 219 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.35-1 TRAPMGR[175050544]: traputil.c(475) 205 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:05 192.168.1.39-1 TRAPMGR[126209216]: traputil.c(466) 181 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.43-1 TRAPMGR[175061168]: traputil.c(475) 280 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.48-1 TRAPMGR[175067568]: traputil.c(475) 446 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.52-1 TRAPMGR[175072816]: traputil.c(475) 261 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.2-1 TRAPMGR[138984840]: traputil.c(475) 431 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.4-1 TRAPMGR[138984160]: traputil.c(475) 246 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.8-1 TRAPMGR[138983208]: traputil.c(475) 265 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.11-1 TRAPMGR[138983560]: traputil.c(475) 328 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:07 192.168.1.15-1 TRAPMGR[138983728]: traputil.c(475) 220 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.14-2 TRAPMGR[138984192]: traputil.c(475) 204 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.35-1 TRAPMGR[175050544]: traputil.c(475) 206 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:06 192.168.1.39-1 TRAPMGR[126209216]: traputil.c(466) 182 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.43-1 TRAPMGR[175061168]: traputil.c(475) 281 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.48-1 TRAPMGR[175067568]: traputil.c(475) 447 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.52-1 TRAPMGR[175072816]: traputil.c(475) 262 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.2-1 TRAPMGR[138984840]: traputil.c(475) 432 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.4-1 TRAPMGR[138984160]: traputil.c(475) 247 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.8-1 TRAPMGR[138983208]: traputil.c(475) 266 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.11-1 TRAPMGR[138983560]: traputil.c(475) 329 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.14-2 TRAPMGR[138984192]: traputil.c(475) 205 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.15-1 TRAPMGR[138983728]: traputil.c(475) 221 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.35-1 TRAPMGR[175050544]: traputil.c(475) 207 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:08 192.168.1.39-1 TRAPMGR[126209216]: traputil.c(466) 183 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.43-1 TRAPMGR[175061168]: traputil.c(475) 282 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.48-1 TRAPMGR[175067568]: traputil.c(475) 448 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.52-1 TRAPMGR[175072816]: traputil.c(475) 263 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.2-1 TRAPMGR[138984840]: traputil.c(475) 433 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.4-1 TRAPMGR[138984160]: traputil.c(475) 248 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.8-1 TRAPMGR[138983208]: traputil.c(475) 267 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.11-1 TRAPMGR[138983560]: traputil.c(475) 330 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.14-2 TRAPMGR[138984192]: traputil.c(475) 206 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.15-1 TRAPMGR[138983728]: traputil.c(475) 222 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:10 192.168.1.35-1 TRAPMGR[175050544]: traputil.c(475) 208 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:09 192.168.1.39-1 TRAPMGR[126209216]: traputil.c(466) 184 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:11 192.168.1.43-1 TRAPMGR[175061168]: traputil.c(475) 283 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26
<166>Feb 1 10:33:11 192.168.1.48-1 TRAPMGR[175067568]: traputil.c(475) 449 %% Authent. Failure: Unit: 1 IP Address: 192.168.7.26

Ubuntu/Debian: Eigene Rootzertifikate importieren und vertrauen

Um Zertifikatsfehler bei eigenen CAs zu beseitigen muss man das Root CA Zertifikat dem Zertifikatsstore des Betriebssystems bekannt machen.

Das Root CA Zertifikat muss im Ordner /usr/share/ca-certificates abgelegt werden mit der Endung .crt (nicht .pem). Man sollte einen passenden und aussagekräfigen Namen für die Datei vergeben. Bei Ubuntu sind zusätzlich die Zertifikate in verschiedene Unterordner einsortiert. Wenn es mehrere sind kann man auch noch einen eigenen Ordner erstellen.

root@foobar:/usr/share/ca-certificates# ls -la
insgesamt 52
drwxr-xr-x  11 root root  4096 2011-07-29 12:29 .
drwxr-xr-x 101 root root  4096 2011-07-28 11:10 ..
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 brasil.gov.br
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 cacert.org
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 debconf.org
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 gouv.fr
drwxr-xr-x   2 root root 12288 2011-07-28 10:16 mozilla
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 signet.pl
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 spi-inc.org
drwxr-xr-x   2 root root  4096 2011-07-28 10:16 telesec.de

Nach dem Ablegen des Zertifikats muss man dieses noch installieren. Dazu ist das Kommando „dpkg-reconfigure ca-certificates“ auszuführen. Es öffnet sich ein Menü, hier ist sicherheitshalber „Fragen“ auszuwählen.

 ┌──────────────────────────────┤ ca-certificates configuration ├───────────────────────────────┐
 │ Dieses Paket kann neue Zertifikate von CAs (Zertifizierungsstellen) installieren, wenn ein   │
 │ Upgrade durchgeführt wird. Sie sollten solche neuen CA-Zertifikate vielleicht prüfen und     │
 │ nur Zertifikate auswählen, denen Sie vertrauen.                                              │
 │                                                                                              │
 │  - Ja    : neuen CA-Zertifikaten wird vertraut und sie werden installiert;                   │
 │  - Nein  : neue CA-Zertifikate werden standardmäßig nicht installiert;                       │
 │  - Fragen: fragt bei jedem neuen CA-Zertifikat nach.                                         │
 │                                                                                              │
 │ Neuen Zertifikaten von Zertifizierungsstellen vertrauen?                                     │
 │                                                                                              │
 │                                            Ja                                                │
 │                                            Nein                                              │
 │                                            Fragen                                            │
 │                                                                                              │
 │                                                                                              │
 │                                            <Ok>                                              │
 │                                                                                              │
 └──────────────────────────────────────────────────────────────────────────────────────────────┘

Im nächsten Screen muss das zu installierende Zertifikat markiert werden.

 

┌──────────────────────────────┤ ca-certificates configuration ├──────────────────────────────┐
│ Dieses Paket installiert gebräuchliche Zertifikate von CAs (Zertifizierungsstellen) unter   │
│ /usr/share/ca-certificates.                                                                 │
│                                                                                             │
│ Bitte wählen Sie die Zertifizierungsstellen aus, denen Sie vertrauen, damit deren           │
│ Zertifikate in /etc/ssl/certs installiert werden. Sie werden in eine einzige Datei          │
│ /etc/ssl/certs/ca-certificates.crt zusammengestellt.                                        │
│                                                                                             │
│ Zu aktivierende Zertifikate:                                                                │
│                                                                                             │
│    [*] brasil.gov.br/brasil.gov.br.crt                                                  ↑   │
│    [*] cacert.org/cacert.org.crt                                                        ▮   │
│    [*] debconf.org/ca.crt                                                               ▒   │
│    [*] gouv.fr/cert_igca_dsa.crt                                                        ▒   │
│    [*] gouv.fr/cert_igca_rsa.crt                                                        ▒   │
│    [ ] eigene-root-ca/root-ca.crt                                                       ▒   │
│    [*] mozilla/ABAecom_=sub.__Am._Bankers_Assn.=_Root_CA.crt                            ▒   │
│    [*] mozilla/AddTrust_External_Root.crt                                               ↓   │
│                                                                                             │
│                                                                                             │
│                                           <Ok>                                              │
│                                                                                             │
└─────────────────────────────────────────────────────────────────────────────────────────────┘

Das ganze mit OK bestätigen, das Zertifikat wird installiert.

Postfix: Mail-Relay mit SMTP-Auth via Submission/TLS für ausgehende Mails

Ein Server soll als Relayhost (oder in der Windowswelt auch Smarthost gennant) dienen. Wer keine feste IP Adresse mit passenden DNS Reverse Eintrag hat wird schlechte Chancen haben das ein richtig konfigurierter Mailserver die Mails annehmen wird. Die Lösung ist die Mails an einen anderen Relayhost oder Mailserver zu schicken und dieser stellt dann die Mails zu. Manchmal bieten die Provider selbst Relayhosts an in vielen Fällen bleibt nichts anderes übrig als das ganze über ein Postfach zu versenden. Einige Provider bieten das Anliefern von Mails nur noch über Submission (TCP/587) und TLS verschlüsselt an. Ist mir persönlich auch lieber …

Beispielszenario

Provider: example.net

Mailaccount: user1@example.net Password: example%1

Mailserver: mail.example.net

Submission (TCP/587) und TLS sind notwendig.

 Lösung

„/etc/postfix/main.cf“ ergänzen mit:

relayhost = mail.example.net:submission
smtp_use_tls=yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/smtp_auth
smtp_sasl_security_options = noanonymous, noplaintext
smtp_sasl_tls_security_options = noanonymous

/etc/postfix/smtp_auth (Map-Datei, muss initialisiert werden!!!)

mail.example.net:submission               user1@example.net:example%1

Folgende Kommandos sind noch auszuführen:

postmap /etc/postfix/smtp_auth

durchführen um die Datei zu mappen.

/etc/init.d/postfix restart

durchführen um die Änderungen zu aktivieren.

Massenhafte Änderungen von Datei und Verzeichnis Rechten unter Linux

Ich habe gerade das Tool OpenTimeTool auf einem Server installiert. Im Paket sind alle Dateien und Verzeichnisse mit 777 Rechten vorhanden. Man sollte wenn die Kiste im Internet steht die Berechtungungen schon ein wenig restriktiver vergeben. Normalerweise haben Dateien auf einem Webserver eine 644 Maske und Verzeichnisse eine 755 Maske.

foo@bar:/var/www/bar/opentimetool/test# ls -la
insgesamt 2404
drwxr-xr-x  7 root   root      4096 2011-07-21 15:10 .
drwxr-xr-x  8 foo    foo       4096 2011-07-21 15:10 ..
-rwxrwxrwx  1 root   root      9513 2011-04-18 22:12 CHANGELOG
drwxrwxrwx  3 root   root      4096 2011-04-18 22:25 classes
-rwxrwxrwx  1 root   root      1371 2008-10-21 21:20 config-local.php.dist
-rwxrwxrwx  1 root   root      8179 2011-04-18 22:12 config.php
-rwxrwxrwx  1 root   root      3642 2011-01-05 00:10 db_upgrade.php
drwxrwxrwx  3 root   root      4096 2011-04-18 22:25 docs
drwxrwxrwx  9 root   root      4096 2011-04-18 22:25 htdocs
drwxrwxrwx 15 root   root      4096 2011-04-18 22:25 includes
-rwxrwxrwx  1 root   root      1546 2008-03-18 15:45 index.php
-rwxrwxrwx  1 root   root     22871 2011-01-05 00:10 init.php
-rwxrwxrwx  1 root   root      2983 2008-09-22 18:51 INSTALL
-rwxrwxrwx  1 root   root      2403 2008-09-22 18:51 INSTALL.de
-rwxrwxrwx  1 root   root     18009 2008-03-18 15:45 LICENSE
-rwxrwxrwx  1 root   root        17 2008-03-18 15:45 media
-rwxrwxrwx  1 root   root       856 2008-03-18 15:45 messages.php
-rwxrwxrwx  1 root   root       717 2008-10-21 21:20 mobile_access.README
drwxrwxrwx  2 root   root      4096 2011-04-18 22:25 mobile_login
-rwxrwxrwx  1 root   root     63312 2010-09-09 10:24 mysql.sql
-rw-r--r--  1 root   root   2201345 2011-04-27 22:02 openTimetool_2.3.1.tar.gz
-rwxrwxrwx  1 root   root       214 2008-03-18 15:45 phpinfo.php
-rwxrwxrwx  1 root   root       676 2008-03-18 15:45 README
-rwxrwxrwx  1 root   root       915 2008-03-18 15:45 SafeMode.README
-rwxrwxrwx  1 root   root       709 2008-03-24 21:18 Suhosin-README.txt
-rwxrwxrwx  1 root   root     10215 2008-03-18 15:45 TODO
-rwxrwxrwx  1 root   root     10761 2010-02-09 09:59 translate_de.sql
-rwxrwxrwx  1 root   root      9243 2010-02-09 09:59 translate_en.sql
-rwxrwxrwx  1 root   root       982 2010-02-09 09:59 update.README

Mit den Linuxboardmitteln kann man sich viel Arbeit sparen. Mit „find“ kann ich einen Parameter -type mitgeben wo ich entweder Dateien (f = Files) oder Verzeichnisse (d = Directory) als Ergebnis zurückgeben lassen kann. Mit dem Parameter -exec kann ich anschließend direkt ein Kommando ausführen und das gefundene als Parameter ({}) mitgeben.

find /var/www/bar/opentimetool/test -type f -exec chmod 644 {} \;
find /var/www/bar/opentimetool/test -type d -exec chmod 755 {} \;

Zuletzt sollte man noch den Benutzer und die Gruppe richtig setzen.

chown www-data:www-data -R /var/www/bar/opentimetool/test

Das Resultat sieht dann wie folgt aus:

foo@bar:/var/www/opentimetool/test# ls -la
insgesamt 2404
drwxr-xr-x  7 www-data www-data    4096 2011-07-21 15:10 .
drwxr-xr-x  8 foo      foo         4096 2011-07-21 15:10 ..
-rw-r--r--  1 www-data www-data    9513 2011-04-18 22:12 CHANGELOG
drwxr-xr-x  3 www-data www-data    4096 2011-04-18 22:25 classes
-rw-r--r--  1 www-data www-data    1371 2008-10-21 21:20 config-local.php.dist
-rw-r--r--  1 www-data www-data    8179 2011-04-18 22:12 config.php
-rw-r--r--  1 www-data www-data    3642 2011-01-05 00:10 db_upgrade.php
drwxr-xr-x  3 www-data www-data    4096 2011-04-18 22:25 docs
drwxr-xr-x  9 www-data www-data    4096 2011-04-18 22:25 htdocs
drwxr-xr-x 15 www-data www-data    4096 2011-04-18 22:25 includes
-rw-r--r--  1 www-data www-data    1546 2008-03-18 15:45 index.php
-rw-r--r--  1 www-data www-data   22871 2011-01-05 00:10 init.php
-rw-r--r--  1 www-data www-data    2983 2008-09-22 18:51 INSTALL
-rw-r--r--  1 www-data www-data    2403 2008-09-22 18:51 INSTALL.de
-rw-r--r--  1 www-data www-data   18009 2008-03-18 15:45 LICENSE
-rw-r--r--  1 www-data www-data      17 2008-03-18 15:45 media
-rw-r--r--  1 www-data www-data     856 2008-03-18 15:45 messages.php
-rw-r--r--  1 www-data www-data     717 2008-10-21 21:20 mobile_access.README
drwxr-xr-x  2 www-data www-data    4096 2011-04-18 22:25 mobile_login
-rw-r--r--  1 www-data www-data   63312 2010-09-09 10:24 mysql.sql
-rw-r--r--  1 www-data www-data 2201345 2011-04-27 22:02 openTimetool_2.3.1.tar.gz
-rw-r--r--  1 www-data www-data     214 2008-03-18 15:45 phpinfo.php
-rw-r--r--  1 www-data www-data     676 2008-03-18 15:45 README
-rw-r--r--  1 www-data www-data     915 2008-03-18 15:45 SafeMode.README
-rw-r--r--  1 www-data www-data     709 2008-03-24 21:18 Suhosin-README.txt
-rw-r--r--  1 www-data www-data   10215 2008-03-18 15:45 TODO
-rw-r--r--  1 www-data www-data   10761 2010-02-09 09:59 translate_de.sql
-rw-r--r--  1 www-data www-data    9243 2010-02-09 09:59 translate_en.sql
-rw-r--r--  1 www-data www-data     982 2010-02-09 09:59 update.README

socat: Pfiffiges Tool zum Mappen von IPv6 auf IPv4 Adressen

Wer gerade vor dem Problem steht alle Dienste auf seinen Linux Maschinen IPv4/IPv6 Dualstack fähig zu machen, der ist froh wenn er so ein Tool wie socat findet.

Es sind zwar die meisten Linux Dienste bereits Dualstack fähig aber es gibt vereinzelt noch Dienste wo keine regelmäßige bis keine Pflege der Software stattfindet.

socat ist quasi ein IPv6 -> IPv4 Relay. Das Paket ist im Packet Repository von Debian / Ubuntu verfügbar.

Beschreibung von socat aus der README des Pakets:

socat is a relay for bidirectional data transfer between two independent data
channels. Each of these data channels may be a file, pipe, device (serial line
etc. or a pseudo terminal), a socket (UNIX, IP4, IP6 - raw, UDP, TCP), an
SSL socket, proxy CONNECT connection, a file descriptor (stdin etc.), the GNU
line editor (readline), a program, or a combination of two of these.
These modes include generation of "listening" sockets, named pipes, and pseudo
terminals.

socat can be used, e.g., as TCP port forwarder (one-shot or daemon), as an
external socksifier, for attacking weak firewalls, as a shell interface to UNIX
sockets, IP6 relay, for redirecting TCP oriented programs to a serial line, to
logically connect serial lines on different computers, or to establish a
relatively secure environment (su and chroot) for running client or server
shell scripts with network connections.

Many options are available to refine socats behaviour:
terminal parameters, open() options, file permissions, file and process owners,
basic socket options like bind address, advanced socket options like IP source
routing, linger, TTL, TOS (type of service), or TCP performance tuning.

More capabilities, like daemon mode with forking, client address check,
"tail -f" mode, some stream data processing (line terminator conversion),
choosing sockets, pipes, or ptys for interprocess communication, debug and
trace options, logging to syslog, stderr or file, and last but not least
precise error messages make it a versatile tool for many different purposes.

In fact, many of these features already exist in specialized tools; but until
now, there does not seem to exists another tool that provides such a generic,
flexible, simple and almost comprehensive (UNIX) byte stream connector.

Das Tool kann noch wesentlich mehr, Infos findet man auf der Webseite des Programmierers http://www.dest-unreach.org/socat/.

Ich habe das für meinen alten SKS Keyserver verwendet, der noch kein IPv6 beherrscht. Ich lasse das ganze in einzelnen Screens laufen.

screen -dmS socat1 socat TCP6-LISTEN:11370,ipv6only=1,reuseaddr,fork TCP4:1.2.3.4:11370
screen -dmS socat2 socat TCP6-LISTEN:11371,ipv6only=1,reuseaddr,fork TCP4:1.2.3.4:11371
screen -dmS socat3 socat TCP6-LISTEN:80,ipv6only=1,reuseaddr,fork TCP4:1.2.3.4:11371

Damit das ganze auch noch einen Neustart überlebt kann man es noch in die /etc/rc.local vor dem „exit 0“ einfügen.

Managed Service Accounts (MSA) für Services unter Windows 2008 R2

Unter Windows 2008 R2 gibt es sog. Managed Service Accounts. Diese Accounts sind für Dienste / Services gedacht. Vorteil dieser Accounts ist, das diese keine festen Passwörter mehr benötigen. Früher wurde für einen Service ein Account mit einem festen Passwort angelegt das nicht abläuft. Mit dieser neuen Funktion ist das nicht mehr notwendig da der Server selbständig in der Lage ein neues Passwort zu vergeben wenn es ausläuft.

Für die Erstellung von MSA´s benötigt man die PowerShell im Adminmodus.

Benötigt wird das Modul ActiveDirectory.

import-module activedirectory
New-ADServiceAccount -name msa1
New-ADServiceAccount -name msa2 -AccountPassword (ConvertTo-SecureString -AsPlainText 1q2w3e4r%T -Force)
Add-ADComputerServiceAccount -Identity romulus -ServiceAccount msa1
Add-ADComputerServiceAccount -Identity romulus -ServiceAccount msa2

New-ADServiceAccount legt einen neuen MSA an. Falls man für eine Dienstinstallation ein Passwort benötigt kann man mit dem Zusatz -AccountPasswort (siehe MSA2) ein Startpasswort definieren. Nach Ablauf der Passwortgültigtkeit wird dann ebenfalls ein neues gesetzt. Mit Add-ADComputerServiceAccount wird festgelegt für welche Maschine der Serviceaccount ist.

Auf der Maschine wo der ServiceAccount benötigt wird muss dieser noch Installiert werden.

Import-Module ActiveDirectory
Install-ADServiceAccount -Identity msa1
Install-ADServiceAccount -Identity msa2

Mit dem Kommando Install-ADServiceAccount wird der Maschine der ServiceAccount bekannt gemacht. Ab diesen Moment weiß die Maschine das sie für diesen Account auch die Passwörter wechseln muss wenn es soweit ist.

 Wie werden die MSA`s verwendet?

Zum Testen habe ich einfach schnell 2 Dienste über die Kommandozeile angelegt.

Wichtig sind die Leerzeichen nach dem Istgleich.

sc create Dienst1 type= own binpath= c:\dienst1.exe
sc create Dienst2 type= own binpath= c:\dienst2.exe

Jetzt benötigen wir die services.msc Managementkonsole, dort tauchen jetzt auch unsere 2 neuen Dienste auf.

Einen der Testdienste mit Doppelklick öffnen und in den Reiter „Logon“ gehen. Hier „This Account“ auswählen und auf „Browse“ gehen. In das Suchfeld msa1 oder msa2 eingeben und suchen. Das ganze mit OK bestätigen.

Der Account wird als msa1$ oder msa2$ angezeigt. Passwort kann man irgendwas eingeben, um das Passwort kümmert sich Windows selbst. Alles mit OK bestätigen.

Noch ein kleines „Nice to know“

Es gibt die Möglichkeit ACLs für Dienste selbst zu vergeben, d.H. nicht nur für Dienste die unter einem User laufen sondern auch für Dienste die im LOCAL SYSTEM laufen. Hier kann man den Diensten Rechte nehemen oder geben.

Als Location muss die Maschine selbst angegeben werden, in dem Fall „ROMULUS“. Im Suchfeld muss der entsprechende Dienst mit dem Prefix „nt service\“ eingegeben werden, also „nt service\dienst1“. Ohne das Prefix findet Windows den Dienst nicht!

Auf einen Dienst können alle ACLs die auf einen User anwendbar sind ebenfalls angewendet werden.

Die PowerShell

Dieser Artikel ist nur ein kleiner Überblick der mächtigen PowerShell. Die PowerShell wurde von Microsoft entwickelt um administrative Aufgaben effizienter erledigen zu können und um eine zentrale, einheitliche Schnittstelle zu bieten. Für viele neue Microsoft Produkte ist die PowerShell unabdingbar (z.B. Microsoft Exchange 2010).

Allgemeines

Die PowerShell besteht aus kleinen Einheiten die sich Cmdlets nennen. Die Kommandos sind alle nach dem Schema Verb-Substantiv aufgebaut, z.B. get-help. Groß-/Kleinschreibung spielt keine Rolle. Für bestimmte Kommandos und Cmdlets gibt es Aliase.

 

Wichtige Kommandos

 

Kommando/Cmdlet Beschreibung
get-help Zeigt die Hilfe an
get-help <Kommando/Cmdlet> Zeigt die Hilfe zu einem Kommando/Cmdlet an
get-variable Zeigt alle gesetzten Variablen an
get-alias Zeit alle gesetzten Aliase an
get-module -ListAvailable Zeigt vorhandene Module an
import-module <modulname> Importiert das Modul in die aktuelle Sitzung (z.B. import-module activedirectory)
get-command Zeigt die verfügbaren Kommandos / Cmdlets an
sort-object Sortieren einer Datenmenge
select-object Einen bestimmten Bereich selektieren (z.B. select-object -First 3)
export-csv Datenmenge in CSV exportieren
get-psdrive Zeigt PowerShell Laufwerke an
invoke-command Kommandos Remote auf anderen Server ausführen
enable-psremoting Aktiviert Remote Power Shell
new-pssession Baut eine Verbindung zu einem Remote Power Shell Server auf
enter-pssession Geht in die Remote Verbindung rein
exit-pssession Verlässt die Remote Session
get-credential Frägt über einen Popup Anmeldeinformationen ab wenn man nicht die Sitzungsanmeldeinformationen verwenden will
get-aduser <username> Frägt aus dem AD Informationen über einen User ab (Nur Verfügbar wenn das ActiveDirectory Modul geladen ist.)
get-adobject <filter> Frägt ein Objekt aus dem AD ab (Nur Verfügbar wenn das ActiveDirectory Modul geladen ist.)
get-adcomputer <computer> Frägt aus dem AD Computerinformationen ab (Nur Verfügbar wenn das ActiveDirectory Modul geladen ist.)
set-adaccountpassword Setzt Passwort für einen User (Nur Verfügbar wenn das ActiveDirectory Modul geladen ist.)

Vergleichs- und Verknüpfungsoperatoren

 

Operator Beschreibung
-eq (equal) gleich
-ne (not equal) nicht gleich
-gt (greater than) größer als
-lt (less than) kleiner als
-ge (greater equal) größer gleich
-le (less equal) kleiner gleich
-like wie .z.B. „a*“
-notlike nicht wie z.B. „a*“
-match „^ABC“ (Regex)
-AND und
-OR oder
-NOT nicht

Beispiel: get-service

get-service gibt den Status der Prozesse zurück.

Will man jetzt z.B. nur Dienste sehen die sich im Status „Running“ befinden so kann man das Ergebnis an ein anderes Kommando pipen.

get-service | where-object {$_.status -eq "running"}

Das Resultat:

Alternativ für where-object gibt es einen Alias der „?“ heißt. Also wäre auch folgender Befehl möglich:

get-service | ? {$_.status -eq "running"}

Das ganze lässt sich mit einem Verbindungsoperator noch erweitern:

get-service | ? {($_.name -like "A*") -AND ($_.status -like "run*")}

Als Resultat bekommen wir alle Prozesse die mit A…. beginnen und laufen.

Jetzt können wir uns noch alle Prozesse mit A ausgeben lassen und diese absteigend sortieren.

get-service | ? {$_.name -like "a*"} | sort-object displayname -Descending

Die Ausgabe können wir jetzt noch auf 3 Resultate beschränken:

get-service | ? {$_.name -like "a*"} | sort-object displayname -Descending | select-object -First 3

Das können wir jetzt noch in eine CSV Datei exportieren:

get-service | ? {$_.name -like "a*"} | sort-object displayname -Descending | select-object -First 3 | export-csv -path export.csv

Inhalt export.csv:

#TYPE System.ServiceProcess.ServiceController
"Name","RequiredServices","CanPauseAndContinue","CanShutdown","CanStop","DisplayName","DependentServices","MachineName","ServiceName","ServicesDependedOn","ServiceHandle","Status","ServiceType","Site","Container"
"AudioEndpointBuilder","System.ServiceProcess.ServiceController[]","False","False","True","Windows Audio Endpoint Builder","System.ServiceProcess.ServiceController[]",".","AudioEndpointBuilder","System.ServiceProcess.ServiceController[]","SafeServiceHandle","Running","Win32ShareProcess",,
"AudioSrv","System.ServiceProcess.ServiceController[]","False","False","True","Windows Audio","System.ServiceProcess.ServiceController[]",".","AudioSrv","System.ServiceProcess.ServiceController[]","SafeServiceHandle","Running","Win32ShareProcess",,
"AppMgmt","System.ServiceProcess.ServiceController[]","False","False","False","Application Management","System.ServiceProcess.ServiceController[]",".","AppMgmt","System.ServiceProcess.ServiceController[]","SafeServiceHandle","Stopped","Win32ShareProcess",,

PowerShell Drives

In der PowerShell kann man auf verschiedene Daten über das Filesystem zugreifen. Darunter sind unter anderem der Certificate Store und die Registry.

 

get-psdrive
cd HKLM:
cd .\SOFTWARE
dir

Remote PowerShell

In der PowerShell ist es möglich einen RemoteAccess zu aktivieren. Einmal aktiviert bleibt er auch aktiviert, er lässt sich aber wieder abschalten. Der PowerShell Remote Service läuft auf 2 TCP Ports. TCP/5985 ohne SSL und TCP/5986 mit SSL.

Aktivieren der Remote Shell:

enable-psremoting

Die beide Abfragen mit „Y“ bestätigen, eine automatische Firewallregel wird installiert.

Es gibt 2 Möglichkeiten mit der Remote PowerShell zu arbeiten.

1. Wir können ein Kommandosatz übermitteln und das Ergebnis verarbeiten

invoke-command -computername romulus.thoma.local -scriptblock {get-service}

2. Wir können uns eine vollständige Remote Shell öffnen

new-pssession -computername romulus

Nach dem Verbindungsaufbau muss man in die Session einsteigen

enter-pssession 1

Am Beginn der Zeile steht jetzt der Hostname der Remotemaschine. Um die Remotemaschine zu verlassen benötigt man folgendes Kommando:

exit-pssession

Die Sessions verschwinden nach 15 Minuten von alleine, wer nicht warten will kann mit

remove-pssession 1

die Session löschen.

Mit anderen Credentials Remote anmelden

Es gibt 2 verschiedene Möglichkeiten Credentials mitzugeben.

1. Im Script

Windows benötigt gehashte Passwörter für die Anmeldung.

$secpasswd = Convert-To-SecureString "1q2w3e4r%T" -AsPlainText -Force
$username="max"
$mycreds = new-object system.management.automation.pscredential ($username, $secpasswd)
invoke-command -credential $mycreds -computername romulus.thoma.local -scriptblock {get-service}

2. Per Popup

$mycreds = get-credential
invoke-command -credential $mycreds -computername romulus.thoma.local -scriptblock {get-service}

Windows 2008 R2 Server Active Directory Papierkorb aktivieren und verwenden

In der aktuellen Windows 2008 R2 Server Version gibt es die Möglichkeit das gelöschte AD Objekte in einem Papierkorb aufbewahrt werden. Das gab es zwar schon früher, allerdings werden bei dem AD Papierkorb alle Werte erhalten incl. Passwort. Dies war bei der vorhergehenden Lösung nicht der Fall. Der AD Papierkorb setzt voraus das der Forrest Funcional Level „Windows 2008 R2“ ist. Ist die Domäne auf Windows 2003 aufgebaut und wurde zu 2008 R2 migriert werden die Objekte im Papierkorb für 60 Tage gespeichert. Ist die Domäne direkt mit Windows 2008 / 2008 R2 aufgebaut werden die Objekte für 180 Tage gespeichert. Der Wert lässt sich auch ändern.

Wichtig! Die AD Papierkorbfunktion gibt es nur unter Windows 2008 R2 Server.

Um den AD Papierkorb zu aktivieren muss auf dem Schema-Master in einer administrativen Power Shell folgender Befehl ausgeführt werden.

Wer ist der Schema-Master? Dies lässt sich mit netdom ermitteln:

C:\>netdom query fsmo

Falls keine Remote Powershell aktiviert ist muss man sich via RDP verbinden oder sich direkt an die Maschine begeben.

In der PowerShell muss das Modul ActiveDirectory nachgeladen werden. Der DN muss angepasst werden auf die entsprechende Domäne genauso wie das Target.

PS C:\>import-module activedirectory
PS C:\>enable-adoptionalfeature -identity "cn=recycle bin feature,cn=optional features,cn=directory service,cn=windows nt,cn=services,cn=configuration,dc=thoma,dc=local" -scope forestorconfigurationset -target "thoma.local"

ACHTUNG! Das Feature lässt sich nicht mehr abschalten. Anschließend mit „Y“ bestätigen.

 Wie kann man den AD Papierkorb verwenden ?

Ich habe zum Test den User dummy1 gelöscht. Der User befindet sich jetzt in diesem Papierkorb.

Mit einer administrativen PowerShell können wir den User suchen.

PS C:\>import-module activedirectory
PS C:\>get-adobject -filter {name -like "*dummy1*"} -includedeletedobject

Will man diesen User jetzt wiederherstellen so muss man folgenden Befehl verwenden:

PS C:\>get-adobject -filter {name -like "*dummy1*"} -includedeletedobject | restore-adobject

Ruft man jetzt erneut den „get-adobject -filter {name -like „*dummy1*“} -includedeletedobject“ Befehl auf sieht man das der User nichtmehr das Flag „Deleted“ besitzt und wieder seinen orginalen DN hat.

Windows 2008 Active Directory Snapshot erstellen und mounten

Windows 2008 / 2008 R2 Server bietet ein cooles Feature um regelmäßig und schnell das Active Directory zu sichern. Die Sicherung kann im Betrieb erfolgen. Für den Snapshot wird der Volume Shadow Service verwendet.

Zum Anlegen des Snapshots wird das mitgelieferte Tool ntdsutil verwendet.

Wichtig! Das Tool benötigt Admin Rechte. cmd Box mit Adminrechten starten!

Mit dem Kommando

ntdsutil "ac i ntds" "sn" "cr" "q" "q"

verbindet sich das Tool zum Active Directory und startet das Anlegen des Snapshots.

Die bereits erstellten Snapshots kann man sich mit

ntdsutil "sn" "l a" "q" "q"

anzeigen lassen.

 

Um einen Snapshot zu verwenden muss er gemountet werden. Das Mounten erfolgt mit dem Kommando:

ntdsutil "sn" "l a" "mo 10" "q" "q"

10 gibt die Version an die sich auf die Nummer aus der Liste der Snapshots bezieht.

 

Nach dem Mounten ist der Snapshot unter C:\$SNAP_201107141124_VOLUMEC$\ verfügbar. Der Name ist abhängig vom Snapshot.

Aus dem Snapshot können wir jetzt das gesicherte Active Directory parallel starten. Hierzu muss man in den Ordner C:\$SNAP_201107141124_VOLUMEC$\Windows\NTDS\ wechseln. Der Ordner kann abweichen je nachdem wo man die AD Informationen abgelegt hat.

Mit dem Tool dsamain lässt sich die Kopie parallel starten.

Dem Tool dsamain werden 2 Parameter beim Start mitgegeben. Dem Parameter dbpath geben wir unser Active Directory aus dem Snapshot mit. Mit dem Parameter ldapport geben wir noch einen freien Port mit auf den wir uns dann mit einem LDAP Explorer oder mit der dsa.msc verbinden können. Per Default wird das AD aus dem Snapshot im Read Only Modus gestartet.

dsamain -dbpath ntds.dit -ldapport 6500

Jetzt können wir uns mit der dsa.msc auf das gemountete AD verbinden.

Um unser Snapshot AD sehen zu können müssen wir uns lokal auf Port 6500 verbinden.

Hiezu auf die Domäne einen Rechtsklick und „Change Domain Controller“ auswählen.

In diesem Fenster muss bei <Type a Directory Server name[:port] here> localhost:6500 eingetragen werden.

Das ganze mit Ok bestätigen.

Wir sind jetzt mit dem Snapshot AD verbunden und können alles ansehen. Öffnet man ein Objekt stellt man fest das die Objekte, wie oben bereits erwähnt, Read Only sind.

Natürlich kann man auf dieses gemountete AD auch mit anderen Tools wie ldifde, csvde, usw. arbeiten.

Nach der Arbeit sollte man den Snapshot noch unmounten.

Der Unmount kann mit dem folgenden Befehl durchgeführt werden. Hier ist wieder die richtige Index ID einzugeben.

ntdsutil "sn" "l a" "un 10" "q" "q"

Python Version von getadsmtp.pl

Übersetzung des AD Mailadressen Sammelskripts von Perl nach Python.

#!/usr/bin/python

# getadsmtp.py
# Version 1.0
# The script is an translation from the orginal perl script getadsmtp.pl

# This script will pull all users' SMTP addresses from your Active Directory
# (including primary and secondary email addresses) and list them in the
# format "user@example.com OK" which Postfix uses with relay_recipient_maps.
# Be sure to double-check the path to python above.

# This requires python-ldap to be installed.  To install python-ldap on debian based systems,
# at a shell type "apt-get install python-ldap" or "sudo apt-get install python-ldap"

import os, sys, ldap

# Enter the path/file for the output
valid_addresses = "/etc/postfix/example_recipients"

# Enter the FQDN of your Active Directory domain controllers below
dc1="dc01.example.com"
dc2="dc02.example.com"

# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &amp;
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be hqbase="cn=Users,dc=example,dc=com"
# Note:  You can also use just hqbase="dc=example,dc=com"
hqbase="cn=Users,dc=example,dc=com"

# Enter the username &amp; password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire.  Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user@example.com"
user="cn=user,cn=Users,dc=example,dc=com"
passwd="password"

try:
  l = ldap.initialize("ldap://%s" %(dc1))
  l.set_option(ldap.OPT_REFERRALS, 0)
  l.protocol_version = 3
  l.simple_bind_s(user, passwd)

except ldap.LDAPError, e:
  try:
    l = ldap.initialize("ldap://%s" %(dc2))
    l.set_option(ldap.OPT_REFERRALS, 0)
    l.protocol_version = 3
    l.simple_bind_s(user, passwd)

  except ldap.LDAPError, e:
    print "Error connecting to specified domain controllers\n"
    sys.exit()

# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter = "(&amp;(sAMAccountName=*)(mail=*))"
filter = "(&amp; (mailnickname=*) (| (&amp;(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&amp;(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))(&amp;(objectCategory=person)(objectClass=contact))(objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))"

attrs = ["proxyAddresses"]
scope = ldap.SCOPE_SUBTREE

r = l.search(hqbase, scope, filter, attrs)
type,a = l.result(r)
result_set = []

for x in a:
  name,attrs = x
  if hasattr(attrs, 'has_key') and attrs.has_key('proxyAddresses'):
    proxyAddresses = attrs['proxyAddresses']
    for y in proxyAddresses:
      result_set.append("%s OK" %(y.replace("smtp:","").replace("SMTP:","")))

# Add additional restrictions, users, etc. to the output file below.
#result_set.append("user@example.com OK")
#result_set.append("user1@example.com 550 User unknown.")
#result_set.append("bad.example.com 550 User does not exist.")

#######################################################################
# Build file ...
output = file(valid_addresses,'w')

for line in result_set:
  output.write("%s\n" %(line))

output.close()

 

Layer 3 VPN mit OpenSSH

OpenSSH ist in der Lage über Tun-Interfaces ein Layer 3 VPN herzustellen. Dies ist eine sehr einfache Variante, besonders schön ist das man SSH auch in vielen Fällen über einen Proxy verwenden kann.

Szenario

Aufbau:

  • Firewall (intern: 10.10.0.254, extern: 55.66.77.88, macht PAT über externes Interface)
  • SSHVPN VM ist eine VMware Maschine auf meinem VMware Server (intern: 10.10.0.122, tun0: 10.255.255.1, macht PAT über internes Interface)
  • Mein PC (Ubuntu 10.10 Maschine mit NX intern: 10.10.0.80)
  • Mein Netbook in dem Beispiel irgendwo im Internet mit UMTS Stick (Ubuntu 10.10 Netbook Edition)
  • Auf der Firewall ist ein NAT Portforwarding für TCP Port 22 auf 10.10.0.122 eingerichtet

Der Client

Falls noch kein Schlüsselpaar für root existiert muss eines angelegt werden.

toor@sshvpn-vm:~$ sudo ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/root/.ssh/id_rsa.
Your public key has been saved in /home/root/.ssh/id_rsa.pub.
The key fingerprint is:
05:d2:94:2e:16:fd:96:29:5c:6d:13:b5:55:8f:ce:31 root@sshvpn-vm
The key's randomart image is:
+--[ RSA 2048]----+
|      .+o. ..o. +|
|      ..+.. +  +.|
|       + o.+ .E .|
|      o +.=  o o |
|     . .So    o  |
|                 |
|                 |
|                 |
|                 |
+-----------------+
toor@sshvpn-vm:~$

Am einfachsten ist es ohne Passwort. Meine Empfehlung: Unbedingt mit Passwort machen, ist sicherer 😉 Den Namen unverändert lassen, auch den Speicherort. id_rsa wird im .ssh Verzeichnis beim Verbindungsaufbau vom ssh Client automatisch verwendet sofern vorhanden.

Anpassen der /etc/network/interfaces Datei

Folgenden Absatz an das Ende der Datei anfügen.

iface tun0 inet static
      pre-up ssh -S /var/run/sshvpn-tun-ctrl -M -f -w 0:0 55.66.77.88 true
      pre-up sleep 5
      address 10.255.255.2
      pointopoint 10.255.255.1
      netmask 255.255.255.252
      up route add -net 10.10.0.0 netmask 255.255.255.0 gw 10.255.255.1 tun0
      post-down ssh -S /var/run/sshvpn-tun-ctrl -O exit 55.66.77.88

Der Server

Anpassen der /etc/ssh/sshd_config Datei

Hinzufügen / Anpassen folgender Optionen

PermitTunnel point-to-point
PermitRootLogin forced-commands-only

PermitRootLogin existiert bei Ubuntu Systemen (steht auf Yes) schon bei anderen Distris kann das anders sein. Falls die PermitRootLogin existiert die orginale einfach auskommentieren.

Anpassen der /root/.ssh/authorized_keys Datei

tunnel="0",command="/sbin/ifdown tun0;/sbin/ifup tun0" ssh-rsa AAAA.... root@sshvpn-vm

Ab ssh-rsa muss alles ersetzt werden durch den Inhalt aus dem generierten öffentlichen Schüssel vom Client. (/root/.ssh/id_rsa.pub)

Anpassen der /etc/network/interfaces Datei

Folgenden Absatz an das Ende der Datei anfügen.

iface tun0 inet static
      address 10.255.255.1
      netmask 255.255.255.252
      pointopoint 10.255.255.2

IP Forwarding auf dem Server aktivieren

$ sysctl -w net.ipv4.ip_forward=1

Für eine dauerhaftes IP Forwarding muss mann noch die /etc/sysctl.conf anpassen.

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

NAT aktivieren

Ich habe auf meinem SSHVPN Server NAT aktiviert damit ich ohne Routing arbeiten kann, das ist einfacher.

Folgendes Script in /etc/init.d anlegen.

iptables.nat.sshvpn:

/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Aktivieren für Systemstart:

$  update-rc.d iptables.nat.sshvpn defaults

Test des VPN Tunnels

Auf dem Client kann der Tunnel mit folgendem Kommando gestartet werden.

$ sudo ifup tun0

Der SSH Client baut dann die Verbindung zum Server auf und richtet das Tunnelinterface ein.

Ein einfacher Ping zeigt das es funktioniert hat.

$  ping 10.10.0.80
PING 10.10.0.80 (10.10.0.80) 56(84) bytes of data.
64 bytes from 10.10.0.80: icmp_req=1 ttl=64 time=99.57 ms
64 bytes from 10.10.0.800: icmp_req=2 ttl=64 time=90.238 ms
64 bytes from 10.10.0.80: icmp_req=3 ttl=64 time=90.217 ms
64 bytes from 10.10.0.80: icmp_req=4 ttl=64 time=90.181 ms

Viel Spaß mit euren eigenen SSH VPNs 😉

Automatische Proxy Zuweisung über WPAD/PAC

In meiner Testumgebung verwende ich verschiedene Domains die nicht über einen Proxy gehen sollen. Da die manuelle Pflege an meinen Maschinen mir zu umständlich erscheint habe ich mich für den Proxy PAC (Proxy Auto Configuration) weg entschieden. Normalerweise wäre es ja ganz einfach, einfach bei jeder Domain als TLD .local verwenden und als Filter in der  Proxykonfiguration *.local ausschließen. Leider habe ich aber auch .com, usw. im Einsatz und ein Filter auf *.com wäre denkbar unpraktisch 😉 Die Lösung: PAC-File zentral auf meinem Server und bekanntgabe via WPAD.

PAC-File (wpad.dat)

function FindProxyForURL(url, host) {

   var proxy_yes = "PROXY proxy.lanbugs.local:3128";
   var proxy_wlan_yes = "PROXY proxy-wlan.lanbugs.local:3128";
   var proxy_no = "DIRECT";
   var proxy_failback = "PROXY proxy.lanbugs.local:3128; DIRECT";

   // Muss ins Internet direkt
   if (shExpMatch(url,"www.lanbugs.de/*")) {return proxy_yes;}
   if (shExpMatch(url,"www.thoma.cc/*")) {return proxy_yes;}

   // Interne Webseiten
   if (shExpMatch(url,"*.lanbugs.local/*")) {return proxy_no;}
   if (shExpMatch(url, "*.lanbugs.de/*")) {return proxy_no;}
   if (shExpMatch(url, "*.thoma.cc/*")) {return proxy_no;}

   // Homenet
   if (isInNet(host, "10.10.4.0", "255.255.255.0")) {
      return proxy_yes;
   }

   // WLAN
   if (isInNet(host, "10.10.5.0", "255.255.255.0")) {
      return proxy_wlan_yes;
   }

   // Server
   if (isInNet(host, "10.10.4.200", "255.255.255.255"){
     return proxy_no;
   }

   return proxy_failback;
}

Meine PAC Konfiguration mancht folgendes:

  • www.lanbugs.de wird weiterhin zum Proxy gesendet alles andere wird direkt über den internen DNS auf interne Webserver umgeleitet
  • www.thoma.cc wird weiterhin zum Proxy gesendet alles andere wird direkt über den internen DNS aus interne Webserver umgeleitet
  • *.lanbugs.local wird komplett vom Proxy ausgeschlossen
  • Mein Homenet wird komplett über den Proxy proxy.lanbugs.local abgewickelt
  • Mein WLAN Netz wird komplett über den Proxy proxy-wlan.lanbugs.local abgewickelt
  • Für den Server 10.10.4.200 geht nicht über den Proxy
  • Für alle anderen wird versucht über den Proxy zu gehen funktioniert das nicht versucht der Browser es direkt

WPAD Konfiguration

WPAD lässt sich über DNS, DHCP und als manuelles File verteilen, leider unterstützen nicht alle Browser alle Wege.

WPAD Kompatibilitätsliste

Browser DNS DHCP Manuelles File
Internet Explorer 6.0 (SP1) und höher JA JA JA
Firefox 1.5.x JA NEIN JA
Firefox 2.5.x JA NEIN JA
Opera NEIN NEIN JA
NetScape JA NEIN JA
Safari ? ? JA

Vorbereiten des Webservers für WPAD/PAC File

Mein Server läuft mit Ubuntu 8.04 LTS. Bei den Ubuntu / Debian Versionen liegt die MIME Type Konfiguration unter /etc/apache2/mods-enabled.

/etc/apache2/mods-enabled/mime.conf

AddType application/x-ns-proxy-autoconfig .dat
AddType application/x-ns-proxy-autoconfig .pac

Desweiteren sollte die Dateiendung noch in /etc/mime.types bekannt gegeben werden.

application/x-ns-proxy-autoconfig dat
application/x-ns-proxy-autoconfig pac

Das PAC File liegt im root-Verzeichnis des Webservers /var/www als proxy.pac und wpad.dat. Alternativ habe ich noch einen Vorschlag gelesen nur die proxy.pac anzulegen und die Datei via redirect unter wpad.dat erreichbar zu machen. Geschmackssache ….

WPAD via DNS

WPAD via DNS geht über verschiedene Wege. Entweder über einen A/CNAME Record oder über einen TXT/SRV Eintrag. Es kann nicht schaden alle möglichkeiten anzulegen. Per DHCP wird in meinem Netz die Domain lanbugs.local als Primäres DNS Suffix mitgegeben, folglich muss ein Eintrag für wpad.lanbugs.local angelegt werden.

Auszug aus der Zone lanbugs.local:

...
wpad           IN A                       10.10.4.200
wpad.tcp       IN SRV       0 0 80        10.10.4.200
               IN TXT                     "service: wpad:http://wpad.lanbugs.local/wpad.dat"
...

WPAD via DHCP

Der DHCP Server muss mit der Option 252 erweitert werden. In dieser Option wird die URL der wpad.dat eingefügt.

option wpad code 252 = text;

subnet 10.10.4.0 netmask 255.255.255.0 {
…
option wpad “http://wpad.lanbugs.local/wpad.dat ”;
…
}

subnet 10.10.5.0 netmask 255.255.255.0 {
…
option wpad “http://wpad.lanbugs.local/wpad.dat ”;
…
}

WPAD via Manuelles File

Falls alles nicht funktionieren  sollte lässt sich das File auch manuell Eintragen.

Firefox:

Extras -> Einstellungen -> Erweitert -> Netzwerk -> Verbindung (Einstellungen) -> Automatische Proxy-Konfigurations-URL: http://wpad.lanbugs.local/proxy.pac

Internet Explorer:

Extras -> Internet Optionen -> Verindungen -> LAN-Einstellungen (Einstellungen) -> (x) Automatisches Konfigurationsskript verwenden: http://wpad.lanbugs.local/proxy.pac

Chrome:

Nutzt die Einstellungen vom Internet Explorer

Opera:

Einstellungen -> Netzwerk -> Proxyserver -> (x) Automatische Proxykonfiguration verwenden (Skript): http://wpad.lanbugs.local/proxy.pac

Finale

Damit die automatische Zuweisung klappt sollte der Browser auch so eingestellt sein das er eine WPAD Konfiguration zulässt.

Firefox:

Extras -> Einstellungen -> Erweitert -> Netzwerk -> Verbindung (Einstellungen) -> (x) Die Proxy-Einstellungen für dieses Netzwerk automatisch erkennen

Internet Explorer:

Extras -> Internet Optionen -> Verindungen -> LAN-Einstellungen (Einstellungen) -> (x) Automatische Suche der Einstellungen

Chrome:

Nutzt die Einstellugen vom Internet Explorer

Opera:

Hier scheint nur der manuelle Weg zu bleiben

Postfix als Frontend für einen Microsoft Exchange Server

Für einen Kunden habe ich einen Postfix eingerichtet als Frontend Server für seine Exchange Infrastruktur. Die Mailadressen werden automatisch aus dem Active Directory ausgelesen und in einem Hash-File gespeichert. Vorteil ist das der Postfix so nur gültige Mailaliases zulässt. Der Frontend macht auch noch weitere Überprüfungen. (postgrey, policy-weight, etc.)

Folgendes Script wird für das Auslesen der Aliase aus dem AD verwendet:

#!/usr/bin/perl -T -w

# Version 1.02

# This script will pull all users' SMTP addresses from your Active Directory
# (including primary and secondary email addresses) and list them in the
# format "user@example.com OK" which Postfix uses with relay_recipient_maps.
# Be sure to double-check the path to perl above.

# This requires Net::LDAP to be installed.  To install Net::LDAP, at a shell
# type "perl -MCPAN -e shell" and then "install Net::LDAP"

use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );

# Enter the path/file for the output
$VALID = "/etc/postfix/example_recipients";

# Enter the FQDN of your Active Directory domain controllers below
$dc1="domaincontroller1.example.com";
$dc2="domaincontroller2.example.com";

# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be $hqbase="cn=Users,dc=example,dc=com"
# Note:  You can also use just $hqbase="dc=example,dc=com"
$hqbase="cn=Users,dc=example,dc=com";

# Enter the username & password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire.  Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user\@example.com"
$user="cn=user,cn=Users,dc=example,dc=com";
$passwd="password";

# Connecting to Active Directory domain controllers
$noldapserver=0;
$ldap = Net::LDAP->new($dc1) or
   $noldapserver=1;
if ($noldapserver == 1)  {
   $ldap = Net::LDAP->new($dc2) or
      die "Error connecting to specified domain controllers $@ \n";
}

$mesg = $ldap->bind ( dn => $user,
                     password =>$passwd);
if ( $mesg->code()) {
    die ("error:", $mesg->code(),"\n","error name: ",$mesg->error_name(),
        "\n", "error text: ",$mesg->error_text(),"\n");
}

# How many LDAP query results to grab for each paged round
# Set to under 1000 for Active Directory
$page = Net::LDAP::Control::Paged->new( size => 990 );

@args = ( base     => $hqbase,
# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter => "(&(sAMAccountName=*)(mail=*))"
         filter => "(& (mailnickname=*) (| (&(objectCategory=person)
                    (objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))
                    (&(objectCategory=person)(objectClass=user)(|(homeMDB=*)
                    (msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))
                    (objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))",
          control  => [ $page ],
          attrs  => "proxyAddresses",
);

my $cookie;
while(1) {
  # Perform search
  my $mesg = $ldap->search( @args );

# Filtering results for proxyAddresses attributes
  foreach my $entry ( $mesg->entries ) {
    my $name = $entry->get_value( "cn" );
    # LDAP Attributes are multi-valued, so we have to print each one.
    foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) {
     # Test if the Line starts with one of the following lines:
     # proxyAddresses: [smtp|SMTP]:
     # and also discard this starting string, so that $mail is only the
     # address without any other characters...
     if ( $mail =~ s/^(smtp|SMTP)://gs ) {
       push(@valid, $mail." OK\n");
     }
    }
  }

  # Only continue on LDAP_SUCCESS
  $mesg->code and last;

  # Get cookie from paged control
  my($resp)  = $mesg->control( LDAP_CONTROL_PAGED ) or last;
  $cookie    = $resp->cookie or last;

  # Set cookie in paged control
  $page->cookie($cookie);
}

if ($cookie) {
  # We had an abnormal exit, so let the server know we do not want any more
  $page->cookie($cookie);
  $page->size(0);
  $ldap->search( @args );
  # Also would be a good idea to die unhappily and inform OP at this point
     die("LDAP query unsuccessful");
}
# Only write the file once the query is successful
open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!";
print VALID @valid;
# Add additional restrictions, users, etc. to the output file below.
#print VALID "user\@example.com OK\n";
#print VALID "user1\@example.com 550 User unknown.\n";
#print VALID "bad.example.com 550 User does not exist.\n";

close VALID;

Quelle: http://www-personal.umich.edu/~malth/gaptuning/postfix/

Ich habe das Perlscript in /etc/postfix abgelegt. Rechte sollten auch 700 sein. Diese 6 Parameter müssen geändert werden.

$VALID = "/etc/postfix/exchange_recipients";

$dc1="dc01.lanbugs.intra";
$dc2="dc02.lanbugs.intra";

$hqbase="cn=Users,dc=lanbugs,dc=intra";

$user="cn=sys-read-aliases-mailfront,cn=Users,dc=lanbugs,dc=intra";
$passwd="geheim123";

Das Script wird als Cronjob jede Stunde ausgeführt. Hierzu muss nur ein File mit dem Namen run_read_aliases_from_exchange.sh mit den Rechten 700 im Verzeichnis /etc/cron.hourly angelegt werden. (Debian/Ubuntu)

run_read_aliases_from_exchange.sh:

#!/bin/sh

cd /etc/postfix ; ./getadsmtp.pl && postmap exchange_recipients

In der /etc/postfix/main.cf kann dann das neue Hash-File als relay_recipient_map verwendet werden.

main.cf:

...
relay_recipient_maps = hash:/etc/postfix/exchange_recipients
relay_domains = lanbugs.de
transport_maps = hash:/etc/postfix/transport
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.1.1.20/32
...

relay_domains erlaubt allgemein die Domain lanbugs.de. mynetworks wurde noch um die IP-Adresse des Exchange Servers erweitert damit dieser E-Mails zur Außenwelt senden kann. Die transport_maps sorgen dafür dass alle Mails an lanbugs.de an den Exchange Server weiter transportiert werden.

/etc/postfix/transport: (nach dem Anlegen postmap /etc/postfix/transport nicht vergessen!!!!)

langbugs.de      smtp:[10.1.1.20]:25

10.1.1.20 ist der interne Exchange Server.

Viel Spaß mit dem Postfix Frontend 😉

Massenbackup für Switche via Telnet & TFTP

Das Beispiel habe ich für Huaweiswitche erstellt. Es geht scheinbar auch über SNMP aber per Telnet die Befehle absetzen geht bei jedem Hersteller.

Folgende Software wird benötigt:

  • TFTP Server (z.B. 3CDaemon -> http://support.3com.com/software/utilities_for_windows_32_bit.htm)
  • Tera Term (hier ttpmacro -> http://www.heise.de/software/download/teraterm_pro/51776)

Das einzige was man wissen muss sind die Befehle wie man die Konfiguration bei dem entsprechenden Switch sichert. Bei Huawei liegt per Default die Konfiguration in vrpcfg.cfg. Mit dem Kommando tftp x.x.x.x put flash:/vrpcfg.cfg xxxx.cfg kann man die Konfig auf einen TFTP-Server schicken. Das Macro muss also auf verschiedene Situationen warten und dann Kommandos schicken. Das Macro speichert die Konfigurationen unter der jeweiligen IP-Adresse. Eine Erweiterung das noch ein Datum mit in dem Dateinamen eingebaut wird ist auch möglich.

Bei Huawei sieht das wie folgt aus:

>> Username:
<< Username senden
>> Password:
<< Password senden
>> <Switch-1>
<< Komando tftp ... senden
>> <Switch-1>
<< quit senden

Batch-Datei (get_cfgs.bat):

cls
for /F "eol=; tokens=1,2,3,4 delims=, " %%i in (devices.txt) do ( 

teraterm\ttpmacro.exe get_cfgs.ttl %%i

)

Geräte (devices.txt):

172.20.6.11
172.20.6.12
172.20.6.13
...

ttl Macro Script für Tera Term Macro Programm (get_cfg.ttl):

; Host & Port
host = param2

; Username
username = 'admin'

; Password
password = 'admin'

strconcat host ':23 /nossh'
connect host

wait 'Username:'
sendln username
wait 'Password:'
sendln password
wait '>'
kommando = 'tftp 172.20.16.113 put flash:/vrpcfg.cfg '
strconcat kommando param2
strconcat kommando '.cfg'

sendln kommando

wait '>'

sendln 'quit'

Die Macrosprache von Tera Term ist sehr einfach und lässt sich auf andere Hersteller leicht umbiegen.

Viel Spaß 😉

HP Procurve Bash Backup

Howto für Ubuntu und Debian

Mit diesem Script können Massenbackups durchgeführt werden. Das Tool ist mandantenfähig, es legt pro Kunde einen Unterordner an.

Benötigte Pakete
sudo apt-get install snmp tftp-hpa
Mandantenfile
# Hostname,IP,SNMP-RW-Community, (r=running-config,s=startup-config,b=both)
Switch1,10.1.1.1,geheim,r
Switch2,10.1.1.2,geheim,r
Script

hp_load_cfg.sh

#!/bin/bash

#################################################################
# hp_load_cfg.sh - Load HP Procurve Configs via TFTP            #
#################################################################
# Version 1.0.0                                                 #
# Written by Maximilian Thoma (c) 2009 - admin@lanbugs.de       #
#################################################################
# for Ubuntu/Debian - needed snmp and tftp-hpa package          #
#################################################################

# Vars
DATE_TOKEN=$(date +"%Y-%m-%d-%H%M%S")
KUNDE=$(echo $1 | awk -F. '{print $1}')

declare K_DIR=$KUNDE

# Check ob Kundenverzeichnis existiert
if [ ! -e $K_DIR ]; then
mkdir $K_DIR; fi

processLine(){
  line="$@" 

  char_1=${line:0:1}
  if [ $char_1 != '#' ]; then
  ###  echo $line

  # Variablen laden
  SwitchNAME=$(echo $line | awk -F, '{print $1}')
  SwitchIP=$(echo $line | awk -F, '{print $2}')
  SwitchSNMP=$(echo $line | awk -F, '{print $3}')
  SwitchSAV=$(echo $line | awk -F, '{print $4}')

  # Nur running-config
  if [ $SwitchSAV == 'r' ]; then
  echo "###########################################"
  echo "Save $SwitchNAME - IP: $SwitchIP - Running "
  echo "###########################################"
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 2
  tftp $SwitchIP -v -c get running-config $K_DIR/$SwitchNAME-running-$DATE_TOKEN.cfg
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 1
  echo "###########################################"
  fi

  # Nur startup-config
  if [ $SwitchSAV == 's' ]; then
  echo "###########################################"
  echo "Save $SwitchNAME - IP: $SwitchIP - Startup "
  echo "###########################################"
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 2
  tftp $SwitchIP -v -c get startup-config $K_DIR/$SwitchNAME-startup-$DATE_TOKEN.cfg
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 1
  echo "###########################################"
  fi

  # Beides
  if [ $SwitchSAV == 'b' ]; then
  echo "###########################################"
  echo "Save $SwitchNAME - IP: $SwitchIP - Both    "
  echo "###########################################"
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 2
  tftp $SwitchIP -v -c get running-config $K_DIR/$SwitchNAME-running-$DATE_TOKEN.cfg
  tftp $SwitchIP -v -c get startup-config $K_DIR/$SwitchNAME-startup-$DATE_TOKEN.cfg
  snmpset -v 2c -c $SwitchSNMP $SwitchIP 1.3.6.1.4.1.11.2.14.11.5.1.7.1.5.6.0 i 1
  echo "###########################################"
  fi

  fi

}

FILE=""

if [ "$1" == "" ]; then
   FILE="/dev/stdin"
else
   FILE="$1"
   if [ ! -f $FILE ]; then
  	echo "$FILE : does not exists"
  	exit 1
   elif [ ! -r $FILE ]; then
  	echo "$FILE: can not read"
  	exit 2
   fi
fi

BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read line
do
	processLine $line
done
exec 0<&3

IFS=$BAKIFS
exit 0
Script anwenden
./hp_load_cfg.sh Kunde1.txt
Test
./hp_load_cfg.sh Kunde1.txt

###########################################
Save Switch1 - IP: 10.21.73.241 - Running
###########################################
SNMPv2-SMI::enterprises.11.2.14.11.5.1.7.1.5.6.0 = INTEGER: 2
Connected to 10.21.73.241 (10.21.73.241), port 69
getting from 10.21.73.241:running-config to Kunde1/Switch1-running-2009-07-22-234538.cfg [netascii]
Received 748 bytes in 2.3 seconds [2610 bit/s]
SNMPv2-SMI::enterprises.11.2.14.11.5.1.7.1.5.6.0 = INTEGER: 1
###########################################
###########################################
Save Switch2 - IP: 10.21.73.242 - Running
###########################################
SNMPv2-SMI::enterprises.11.2.14.11.5.1.7.1.5.6.0 = INTEGER: 2
Connected to 10.21.73.242 (10.21.73.242), port 69
getting from 10.21.73.242:running-config to Kunde1/Switch2-running-2009-07-22-234538.cfg [netascii]
Received 737 bytes in 1.5 seconds [3981 bit/s]
SNMPv2-SMI::enterprises.11.2.14.11.5.1.7.1.5.6.0 = INTEGER: 1
###########################################

Das Kundenverzeichnis

ls -la Kunde1
...
-rw-r--r-- 1 sysadmin sysadmin  721 2009-07-22 23:45 Switch1-running-2009-07-22-234538.cfg
-rw-r--r-- 1 sysadmin sysadmin  710 2009-07-22 23:45 Switch2-running-2009-07-22-234538.cfg
...

Forwarding für bestimmte DNS Zonen in Bind9

In diesem Szenario hat der Kunde in der DMZ 2 DNS Server (Forwarding zum Provider) stehen. Diese DNS Server sind die „Inernet“ DNS Server. Im internen Netz betreibt der Kunde mehrere Active Directory instanzen. Der Kunde möchte das seine internen DNS Zonen innerhalb der DMZ auflösbar sind. Dies kann man über Zonenweiterleitung in Bind realisieren.

zone "ad01.local" IN {
	type forward;
	forwarders {10.0.0.1; 10.0.0.2;};
};

zone "ad02.local" IN {
	type forward;
	forwarders {10.0.0.3; 10.0.0.4;};
};

zone "msexch.local" IN {
	type forward;
	forwarders {10.0.0.5; 10.0.0.6;};
};

eth Inferfacenummerierung ändern

für Ubuntu / Debian

In bestimmten Fällen will man die Nummerierung der eth Interfaces ändern. Wenn z.B. eine virtuelle Maschine geklont wird und eine neue MAC-Adresse für das Ethernet Interface generiert wird dann würde das Interface eth1 lauten.

Die Zuordnung der Interfaces wird über UDEV realisiert in der Datei /etc/udev/rules.d/70-persistent-net.rules

Nach Änderung der MAC Adresse:

# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.


# PCI device 0x1022:0x2000 (pcnet32)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:15:a5:b6", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

# PCI device 0x1022:0x2000 (pcnet32)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:15:a5:b7", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

00:0c:29:15:a5:b7 soll wieder eth0 werden 00:0c:29:15:a5:b6 gibt es nicht mehr und kann gelöscht werden.

Nach Änderung:

# This file was automatically generated by the /lib/udev/write_net_rules
# program, run by the persistent-net-generator.rules rules file.
#
# You can modify it, as long as you keep each rule on a single
# line, and change only the value of the NAME= key.

# PCI device 0x1022:0x2000 (pcnet32)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:0c:29:15:a5:b7", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"

Neue SSH Hostkeys generieren

für Debian / Ubuntu

Falls man eine VMware Maschine kopiert benötigt man neue SSH Keys. Folgende Zeilen generieren neue Keys.

sudo rm /etc/ssh/ssh_host_key
sudo rm /etc/ssh/ssh_host_key.pub
sudo rm /etc/ssh/ssh_host_rsa_key
sudo rm /etc/ssh/ssh_host_rsa_key.pub
sudo rm /etc/ssh/ssh_host_dsa_key
sudo rm /etc/ssh/ssh_host_dsa_key.pub
sudo /usr/bin/ssh-keygen -t rsa1 -f /etc/ssh/ssh_host_key -N ""
sudo /usr/bin/ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ""
sudo /usr/bin/ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ""

Einrichten einer NPS Policy für HP Procurve Geräte

  • Im Baum auf „Netzwerkrichtlinien“ einen Rechtsklick und „Neu“ auswählen

  • Einen Policy Namen festlegen z.B. „HP Device Access“

  • Folgende Bedinungen werden benötigt
    • Windows-Gruppe: radius_nw_equipment_access
    • NAS-Porttyp: Virtuell (VPN)

  • Zugriff gewähren

  • Haken von MS-CHAPv2 und MS-CHAP entfernen und nur PAP auswählen

  • Hier wird nichts verändert, Weiter…

  • Framed-Protocol und Service-Type entfernen

  • Unter „Standard“ den Typ „Service-Type“ mit dem Attributwert „Administrative“ (Manager) oder „NAS Prompt“ (Operator) hinzufügen

  • Fertigstellen

  • Die Policy mit Rechtsklick „Nach oben“ in die oberste Position befördern

Radius Konfiguration für HP Procurve Geräte

Für operator und manager sollte ein Passwort lokal gesetzt werden falls Radius nicht funktioniert.

aaa authentication login privilege-mode
aaa authentication console login radius local
aaa authentication console enable radius local
aaa authentication telnet login radius local
aaa authentication telnet enable radius local
aaa authentication web login radius local
aaa authentication ssh login radius local
aaa authentication ssh enable radius local
radius-server host 192.168.10.1 key geheim
password manager
password operator

Einrichten einer NPS Policy für Cisco Geräte

  • Im Baum auf „Netzwerkrichtlinien“ einen Rechtsklick und „Neu“ auswählen

  • Einen Policy Namen festlegen z.B. „Cisco Device Access“

  • Folgende Bedinungen werden benötigt
    • Windows-Gruppe: radius_nw_equipment_access
    • Clientanzeigename: CISCO-R?
    • NAS-Porttyp: Virtuell (VPN)

  • Zugriff gewähren

  • Haken von MS-CHAPv2 und MS-CHAP entfernen und nur PAP auswählen

  • Hier wird nichts verändert, Weiter…

  • Framed-Protocol und Service-Type entfernen

  • Unter „Herstellerspezifisch“ den Typ „Cisco-AV-Pair“ mit dem Attributwert „shell:priv-lvl=15“ hinzufügen

  • Fertigstellen

  • Die Policy mit Rechtsklick „Nach oben“ in die oberste Position befördern

Fertig.

Radius Konfiguration für Cisco Geräte

Konfiguration mit Rescue User falls Radius ausfällt. In dem Fall wird mit Username/Passwort und enable-Secret gearbeitet. Im Fall einer Radiusanmeldung arbeitet der User direkt im Privilege Mode 15.

!
aaa new-model
aaa group server radius RADIUS_AUTH
server 192.168.10.1 auth-port 1812 acct-port 1813
!
aaa authentication login networkaccess group RADIUS_AUTH local
aaa authorization exec default group RADIUS_AUTH if-authenticated
aaa authorization exec networkaccess group RADIUS_AUTH local if-authenticated
enable secret 5 $1$UaiX$0ivA6uWDl0eNoZWv4ciLW.
!
username admin privilege 15 secret 5 $1$dArb$Q2sC1uPNaL0QUMlc/V7sF1
ip subnet-zero
!
ip radius source-interface FastEthernet0/1
!
radius-server host 192.168.10.1 auth-port 1812 acct-port 1813 key geheim
radius-server retransmit 3
!
line con 0
 login authentication networkaccess
line vty 0 4
 exec-timeout 0 0
 login authentication networkaccess
line vty 5 15
 exec-timeout 0 0
 login authentication networkaccess
!