Icinga 2 Best Practice Teil 6: Service-Checks auf Agenten schedulen

This entry is part 6 of 6 in the series Icinga 2 Best Practice

In Teil 1 dieser Blogserien beschäftigten wir uns mit Zonen, Agenten und wie man zentral angetriggert Plugins auf diesen Agenten ausführt. Nun im aktuellen Teil soll es darum gehen, das einplanen und ausführen dem Agenten selbst zu überlassen, damit werden auch während eines Verbindungsausfalls weiter Daten gesammelt und später, wenn die Verbindung wieder besteht, nachträglich übertragen.

In dem hier folgenden verdeutlichen wir, wie dies zu konfigurieren ist, mit einem Beispiel von einer Zone master, einer globalen Zone global-templates und einer Zone zu einem Beispiel Agenten agent.example.org.

Die Zonen- und Endpoint-Definitionen des Agenten auf Seite des Masters müssen in einer Datei in der Master-Zone oder in zones.conf hinterlegt werden. Leider ist es hier nicht möglich die jeweiligen Host-, Zonen- und Endpoint-Objekte in einer Datei zusammen zufassen, da wir das Host-Objekt agent.example.org zum Agenten selbst synchronisieren müssen und diese Definition in der Agenten-Zone abgelegt sein muss, z.B. in der Datei zones.d/agent.example.org/agent.conf:

object Host "agent.example.org“ {
 import "generic-host"

 address = "10.0.10.42"
 zone = get_object(Zone, name).parent

 vars.os = "Linux“
}

Durch die Ablage an genau diesem Ort, wird das Objekt zum Agenten synchronisiert. Mit setzen des Attributes zone auf die eigene Parent-Zone sorgen wir jedoch wieder dafür, dass das Host-CheckCommand sowie alle an diesen Host gebundenen Services auf einem Endpoint der Parent-Zone ausgeführt werden, hier die Zone master.

Damit wird der hostalive vom Master ausgeführt, was gewünscht ist, da sonst der Host seine Erreichbarkeit von sich selbst aus testen würde. D.h. alle Services, die auf den Agenten über das Netzwerk zugreifen sollen, bleiben wie sie sind und wir müssen keine Anpassungen vornehmen. Ganz im Gegensatz zu Services, die ein Plugin lokal auf dem Agenten ausführen sollen.

apply Service "load“ {
 import "generic-service“

 check_command = "load“
 if ( get_object(Zone, host.name) ) {
   zone = host.name
 }

 assign where host.vars.os == "Linux"
}

Auch Objekte vom Typ Service besitzen das Attribut zone mit dem wir hier nun die umgekehrten Weg beschreiten, existiert zum Host eine Zone selben Namens, wird die Zone zur Ausführung des zugeordneten Plugins in die eigene Agenten-Zone verlegt.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 – Monitoring automatisiert mit Puppet Teil 5: Konfiguration II

This entry is part 5 of 8 in the series Icinga 2 Monitoring automatisiert mit Puppet

Heute werden wir uns die Define Resource icinga2::object::host zum konfigurieren eines Host-Objekts anschauen und auch Services via apply-Regeln an diesen Host binden.

::icinga2::object::host { 'NodeName':
  target  => '/etc/icinga2/example.d/my_hosts.conf',
  import  => [ 'generic-host' ],
  address => '127.0.0.1',
}

Generell kann bei Objekten mit target angegeben werden in welche Datei sie geschrieben werden. Auch lässt sich mit dem Parameter order Einfluss auf die Reihenfolge der einzelnen Objekte in der jeweiligen Datei nehmen. Der Defaultwert für Host-Objekte ist 50, möchte man nun ein weiteres Host-Objekt davor einfügen, ist bei diesem order auf einen Wert kleiner 50 zu setzen. Das funktioniert mit anderen Objekttypen auf die gleiche Weise. So werden z.B. Hostgruppen oder Services mit einer order von 55 bzw. 60 standardmäßig hinter Hosts einsortiert. Um das mittels import eingebundene Template generic-host vor dem Host in die Datei zu schreiben muss im folgenden Codebeispiel order explizit gesetzt werden, da auch ein Host-Template aus icinga2::object::host erzeugt wird und damit den Defaultwert 50 hat.

::icinga2::object::host { 'generic-host':
  template           => true,
  target             => '/etc/icinga2/example.d/my_hosts.conf',
  order              => '47',
  check_interval     => '1m',
  retry_interval     => 30,
  max_check_attempts => 3,
  check_command      => 'hostalive',
}

Der Titel, also der Name des Objektes, und Werte aller Attribute werden durch einen einfachen Parser ausgewertet. So werden Zahlen als solche erkannt, auch wenn sie in Puppet als String geschrieben sind. Das gilt auch für Zeitabstände wie 1h, 1m oder 1s. Auch Konstanten werden erkannt, dadurch wird NodeName als solche erkannt und in der Icinga-Konfiguration nicht gequotet als Konstanten geschrieben. Die erzeugte Konfigurationsdatei sieht dann wie folgt aus:

template Host "generic-host" {
  check_interval = 1m
  retry_interval = 30
  max_check_attempts = 3
  check_command = "hostalive"
}

object Host NodeName {
  import "generic-host"
  address = "127.0.0.1"
}

Ein einzelnes Service-Objekt wird äquivalent erstellt. Möchte man jedoch mittels Apply den Service mehreren Hosts zuordnen geht dies mit Puppet ebenfalls, der Parameter apply muss hier lediglich auch true gesetzt werden.

::icinga2::object::service { 'ping4':
  target        => '/etc/icinga2/example.d/services.conf',
  apply         => true,
  import        => [ 'generic-service' ],
  check_command => 'ping4',
  assign        => [ 'host.address' ],
}

Die Assign- bzw. Ignore-Ausdrücke werden über die Parameter assign bzw. ignore definiert. Die Werte müssen als Array zugewiesen werden. Aus den einzelnen Elemente werden jeweils Assign- bzw. Ignore-Ausdrücke erzeugt.

apply Service "ping4" to Host {
  import "generic-service"

  check_command = "ping4"
  assign where host.address
}

Custom-Attribute können in beliebiger Anzahl dem Parameter vars als Dictionary-Elemente zugewiesen werden. So erzeugt dieser zusätzlich Puppet-Code für das oben beschriebene Host-Objekt,

  vars => {
    os     => 'Linux',
    disks  => {
      'disk /' => {
        disk_partition => '/',
      },
    },

die folgenden Zeilen Icinga-Konfiguration:

  vars.os = "Linux"
  vars.disks["disk /"] = {
    disk_partition = "/"
  }

Der Parser kann auch komplexere Ausdrücke korrekt bearbeiten, wie in assign des folgenden Services ssh.

::icinga2::object::service { 'ssh':
  target        => '/etc/icinga2/example.d/services.conf',
  apply         => true,
  import        => [ 'generic-service' ],
  check_command => 'ssh',
  assign        => [ '(host.address || host.address6) && host.vars.os == Linux' ],
}

Attribute aus dem Host-Kontext wie auch die Operatoren werden korrekt erkannt, das Wort Linux ist nicht bekannt und damit gequotet dargestellt. Alle bekannten Wörter werden ohne Quotes geschrieben. Bekannte Wörter sind neben dem Objekt-Titel, alle Objekt-Attribute, Custom-Attribute, die Konstanten der Icinga-Instanz und eine in icinga2::params::globals definierte Liste.

apply Service "ssh" to Host {
  import "generic-service"

  check_command = "ssh"
  assign where (host.address || host.address6) && host.vars.os == "Linux"
}

Um nun via Puppet auch die folgende Konfiguration modellieren zu können,

apply Service for (fs => config in host.vars.disks) to Host {
  import "generic-service"

  check_command = "disk"
  vars = vars + config
}

Hierzu setzt man den Parameters apply auf einen String, der dem Zuweisungsteil einer Icinga-Foreach-Schleife für Services entspricht. Wichtig hier zu beachten ist, das fs und config nun auch bekannte Wörter sind und nicht gequotet werden. Nähme man anstatt fs z.B. disk, hätte das zur Folge, das die Zeile check_command, mit disk ohne Quotes geschrieben wird. Damit ist dann aber leider die Konfiguration nicht korrekt, die Validierung schlägt fehl und ein Neustart von Icinga wird nicht ausgeführt.

::icinga2::object::service { 'disk':
  target        => '/etc/icinga2/example.d/services.conf',
  apply         => 'fs => config in host.vars.disks',
  import        => [ 'generic-service' ],
  check_command => 'disk',
  vars          => 'vars + config',
}

Zu den bisher erschienen Artikel dieser Serie geht es hier.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 – Monitoring automatisiert mit Puppet Teil 4: Konfiguration I

This entry is part 4 of 8 in the series Icinga 2 Monitoring automatisiert mit Puppet

Im ersten Teil zur Konfiguration von Objekten, die überwacht werden sollen, widmen wir statischen Dateien, die nicht von Define Resources des Moduls verwaltet werden. Zuerst beschäftigen wir uns jedoch mit dem Parameter confd der Main-Class icinga2. Als Werte werden die Boolean-Werte true und false akzeptiert oder auch eine Pfadangabe. Beim Defaultwert true wird das Verzeichnis /etc/icinga2/conf.d rekursiv in die Konfiguration in /etc/icinga2/icinga2.conf eingebunden. Bei der Verwendung von false entfällt diese Eintrag ersatzlos, hilfreich beim Konfigurieren von verteilten Szenarien der Überwachung.

class { '::icinga2':
  confd => '/etc/icinga2/local.d',
}

Die Angabe eines Pfades, wird das entsprechende Verzeichnis rekursiv als Konfiguration eingelesen. Um die Existenz müssen wir uns jedoch selber kümmern. In diesem Beispiel kopieren wir einmalig die im Paket mitgelieferte Beispielkonfiguration, als Grundlage für weitere Konfigurationen.

file { '/etc/icinga2/local.d':
  ensure  => directory,
  owner   => 'icinga',
  group   => 'icinga',
  mode    => '0750',
  recurse => true,
  replace => false,
  source  => '/etc/icinga2/conf.d',
  tag     => 'icinga2::config::file',
}

Damit diese File- oder Concat-Resources im Zusammenhang mit den anderen Resources in der korrekten Reihenfolge abgearbeitet werden ist das Tag icinga2::config::file von entscheidender Bedeutung. Handelt es sich bei der File-Resource nicht um ein Verzeichnis, wird automatisch ein Reload von icinga2 veranlasst. Letzteres kann unterdrückt werden, indem in der Main-Class das Verwalten des Services ausgeschaltet (manage_service => false) wird, daraus folgt aber, dass man den Service gesondert selbst managen muss.

file { '/etc/icinga2/local.d/my_hosts.conf':
  ensure => file,
  owner  => 'icinga',
  group  => 'icinga',
  mode   => '0640',
  source => 'puppet:///modules/profile/icinga2/my_hosts.conf',
  tag    => 'icinga2::config::file',
}

Das selbe Vorgehen kann analog auch mit einer Concat-Resource aus dem Modul gleichen Namens benutzt werden.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 – Monitoring automatisiert mit Puppet Teil 3: Plugins

This entry is part 3 of 8 in the series Icinga 2 Monitoring automatisiert mit Puppet

Heute gehen wir der Frage nach wann und wie Plugins installiert werden sollten, was besonders wichtig bei Systemen mit icinga Benutzern zum Gegensatz nagios zu beachten ist. Auf z.B. RedHat-Systemen besteht das Problem, dass der Prozess Icinga 2 unter dem Benutzer icinga läuft, aber unteranderem das Plugin check_icmp oder auch check_dhcp nur vom Benutzer root oder einem Mitglied der Gruppe nagios mittels suid-Bit ausgeführt werden können.

# ls -l /usr/lib64/nagios/plugins/check_icmp
-rwsr-x---. 1 root nagios ... /usr/lib64/nagios/plugins/check_icmp

Das Ändern der Gruppenzugehörigkeit mit Puppet ist wenig hilfreich, da leider bei einem Update des Paketes nagios-plugins die alten Berechtigungen wieder hergestellt werden. Man könnte nun natürlich den Benutzer icinga und das Paket nagios-plugins explizit vor der Klasse icinga2 managen, verliert dann jedoch die Paketkontrolle über die uid und muss das Home-Directory, Shell und weitere Eigenschaften per Hand in Puppet entscheiden. Klarer ist die Methode genau diese Sachen dem Paket zu überlassen und erst danach icinga in die Gruppe nagios aufzunehmen.

yumrepo { 'icinga-stable-release':
  ...
}
->
package { [ 'icinga2', 'nagios-plugins' ]:
  ensure => installed,
}
->
user { 'icinga':
  groups => [ 'nagios' ],
}
->
class { '::icinga2':
  manage_package => false,
}

Um dieses Vorhaben umzusetzen ist es erforderlich die benötigten Repositories zuerst einzubinden, hier mit yumrepo angedeutet, dann die Pakete zu installieren, den Benutzer anzupassen und erst dann die Klasse icinga2 zu deklarieren.

Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.

Icinga 2 Best Practice Teil 5: Autosign von Zertifikatsanfragen in verteilten Umgebungen

This entry is part 5 of 6 in the series Icinga 2 Best Practice

Ein jeder kennt das Problem, im Unternehmensnetz gibt es unterschiedliche netzwerkbezogene Sicherheitszonen, die mittels Perimeter voneinander getrennt sind. Für das Monitoring bedeutet dies im Idealfall, man stellt in jeder dieser Zonen einen Icinga-Satelliten bereit, der die von ihm ermittelten Ergebnisse an eine zentrale Instanz weiter meldet, den Icinga-Master. Damit ist gewährleistet, was Firewall-Admins berechtigterweise verlangen, lediglich Punkt-zu-Punkt-Verbindungen zu erlauben. Auch die Richtung des Verbindungsaufbaus ist mit Icinga 2 wählbar.
Setzt man zusätzlich auch Icinga 2 in der Ausprägung Agent ein, benötigt dieser ein signiertes Zertifikat um mit seinem jeweiligen Satelliten oder direkt mit dem Master zu kommunizieren. Im letzten Fall entstehen hieraus keine Probleme. In der Regel wird die CA, in einer Umgebung mit mehreren Satelliten auf dem Master betrieben. Bei lediglich einem Satelliten könnte die CA auch auf genau diesem Satelliten laufen, was jedoch Sicherheitsbedenken hervorruft. Ein Zertifikat aus einer niedrigen Sicherheitszone könnte verwendet werden, um mit einer höheren zu kommunizieren. Gleiches gilt natürlich auch bei der Benutzung lediglich einer CA, aber die Netzwerksicherheit soll ja dieses Risiko Minimieren.
Bleibt das Problem auf einem neuinstallierten Agenten mittels Autosigning ein beglaubigtes Zertifikat zu erhalten. Hier kann eine eigene CA auf jedem Satelliten Abhilfe schaffen. Der jeweilige Agent benötigt nun nur eine Verbindung zu seinem Satelliten um einen Request zu senden und keine Kommunikation zum Master. Wie wird nun dieses genau bewerkstelligt?

  • Erstellen einer CA auf dem Satelliten
  • Der Satellit bekommt ein Zertifikat signiert von seiner eigene CA
  • Der Satellit benötigt sein eigenes RootCA-Zertifikat und das vom Master
  • Der Master bekommt umgekehrt ebenfalls das RootCA vom Satelliten
Lennart Betz

Autor: Lennart Betz

Der diplomierte Mathematiker arbeitet bei NETWAYS im Bereich Consulting und bereichert seine Kunden mit seinem Wissen zu Icinga, Nagios und anderen Open Source Administrationstools. Im Büro erleuchtet Lennart seine Kollegen mit fundierten geschichtlichen Vorträgen die seinesgleichen suchen.