Nachdem ich Icinga2 nun für alle meinen neuen VMs benutze, gehe ich auf jedem Host immer wieder die selben Schritte durch, um die initiale Konfiguration anzulegen. Ich wünschte mir recht schnell ein Skript, das mir möglichst viel Arbeit abnimmt. Da ich in letzter Zeit über verschiedenen Leuten von Ansible gehört habe, hatte ich einen Anlass mir ein erstes Playbook zu schreiben, um Icinga ziemlich einfach auf einem Debian oder Ubuntu-Host zu deployen.
Auf meiner Suche nach einer Vorlage habe ich im Monitoring Portal einen englischsprachigen Beitrag gefunden, aus dem ich mir das Setup der PKI entnommen habe: Using ansible to generate the Icinga Client Certificates. Diesen Teil finde ich am schwersten und ich spare dort im folgenden auch eine genauere Beschreibung aus.
Der erste Schritt im Playbook ist das erzeugen des Tickets auf dem Master, der später für das Setup der PKI benötigt wird:
- hosts: MASTERFQDN
tasks:
- name: generate ticket on the icinga master and save it as a variable
shell: /usr/sbin/icinga2 pki ticket --cn {{ hostitem }}
register: ticket
Interessant für den Ansible Einsteiger: register: ticket legt aus dem Ergebnis des icinga2 Aufrufs auf dem Icinga Master eine Variable an, die später im Playbook auf dem Icinga2 Satellit zum Einsatz kommt.
Als nächstes wird der PGP Key für das Icinga Repository abgelegt, damit im folgenden Schritt das icinga2 Paket direkt aus dem Icinga Repository verwendet werden kann. So erhält man eine aktuelle Icinga Version. 🙂
- name: "Deploy icinga.key"
apt_key:
url: "https://packages.icinga.com/icinga.key"
state: present
- name: "Install Icinga Ubuntu repository"
apt_repository:
repo: deb http://packages.icinga.com/{{ hostvars[hostitem]['icinga_distri'] }} icinga-{{ hostvars[hostitem]['icinga_release'] }} main
state: present
filename: 'icinga'
- name: Update repositories cache and install "icinga2" package
apt:
name: icinga2
update_cache: yes
Das PKI Setup sorgt nun dafür, das der Satellit mit dem Master verschlüsselt reden kann und Master und Satellit wissen, dass sie sich vertrauen können:
- name: create pki folder
file: path=/etc/icinga2/pki state=directory mode=0700 owner=nagios group=nagios
- name: create cert
shell: icinga2 pki new-cert --cn {{ hostitem }} --key /etc/icinga2/pki/{{ hostitem }}.key --cert /etc/icinga2/pki/{{ hostitem }}.crt
- name: save the masters cert as trustedcert
shell: icinga2 pki save-cert --key /etc/icinga2/pki/{{ hostitem }}.key --cert /etc/icinga2/pki/{{ hostitem }}.crt --trustedcert /etc/icinga2/pki/trusted-master.crt --host {{ master_hostname }}
- name: request the certificate from the icinga server
shell: icinga2 pki request --host {{ master_hostname }} --port 5665 --ticket {{ hostvars[master_hostname]['ticket']['stdout'] }} --key /etc/icinga2/pki/{{ hostitem }}.key --cert /etc/icinga2/pki/{{ hostitem }}.crt --trustedcert /etc/icinga2/pki/trusted-master.crt --ca /etc/icinga2/pki/ca.key
- name: node setup
shell: icinga2 node setup --ticket {{ hostvars[master_hostname]['ticket']['stdout'] }} --endpoint {{ master_hostname }} --zone {{ hostitem }} --master_host {{ master_hostname }} --trustedcert /etc/icinga2/pki/trusted-master.crt --cn {{ hostitem }}
Nun wird in der Icinga2 Konfiguration erstmal die vorhandene Konfiguration ausgeblendet, da ich Top-Down Konfiguration durch den Master verwende:
- name: Disable icinga2.conf conf.d
replace:
destfile: /etc/icinga2/icinga2.conf
regexp: '^include_recursive "conf.d"$'
replace: '//include_recursive "conf.d"'
Die zones.conf setze ich aus einem Template:
- name: Setup zones.conf
template:
src: templates/zones.conf.j2
dest: /etc/icinga2/zones.conf
Das passende Template für die Top-Down Konfiguration, die ich verwendet, sieht dabei so aus:
object Zone "global-templates" {
global = true
}
object Endpoint "{{ master_hostname }}" {
host = "{{ master_hostname }}"
}
object Zone "{{ master_hostname }}" {
endpoints = [ "{{ master_hostname }}" ]
}
object Endpoint "{{ hostitem }}" {
host = "{{ hostitem }}"
}
object Zone "{{ hostitem }}" {
endpoints = [ "{{ hostitem }}" ]
parent = "{{ master_hostname }}"
}
Der Satellit muss die Konfiguration und Kommandos akzeptieren:
- name: Accept configuration from master
replace:
destfile: /etc/icinga2/features-enabled/api.conf
regexp: '^(\s+)accept_config = false$'
replace: '\1accept_config = true'
- name: Accept commands from master
replace:
destfile: /etc/icinga2/features-enabled/api.conf
regexp: '^(\s*)accept_commands = false$'
replace: '\1accept_commands = true'
Ist die Konfiguration geschafft kann Icinga2 diese neu einlesen:
- name: Reload Icinga configuration
shell: /etc/init.d/icinga2 reload
Weiter geht es dann auf dem Master, hier muss der Satellit ebenfalls der Zonen Konfiguration hinzugefügt werden. In der zones.conf des Masters wird ein von Ansible markierter Block eingefügt, damit spätere Änderungen an diesem Host mit einem erneuten Playbook Durchlauf eingespielt werden können.
- hosts: MASTERFQDN
tasks:
- name: Append zones.conf
blockinfile:
destfile: /etc/icinga2/zones.conf
marker: "// {mark} ANSIBLE MANAGED BLOCK {{ hostitem }}"
block: |
object Endpoint "{{ hostitem }}" {
host = "{{ hostitem }}"
}
object Zone "{{ hostitem }}" {
endpoints = [ "{{ hostitem }}" ]
parent = "MASTERFQDN"
}
- name: Create zones.d {{ hostitem }} directory
file:
path: /etc/icinga2/zones.d/{{ hostitem }}
state: directory
mode: 0755
owner: nagios
group: nagios
Die initiale Host und Service Konfiguration erfolgt per Template. Das force: no verhindert ein Überschreiben von vorgenommenen Änderungen. Bei Top-Down Konfiguration wird natürlich irgendwann mal was an den Files entsprechend den Anforderungen angepasst. Diese Änderungen möchte ich nicht überschreiben, ein force: no verhindert dies.
- name: Create host.conf
template:
src: templates/icinga-host.conf.j2
dest: /etc/icinga2/zones.d/{{ hostitem }}/host.conf
force: no
- name: Create services.conf
template:
src: templates/icinga-services.conf.j2
dest: /etc/icinga2/zones.d/{{ hostitem }}/services.conf
force: no
Das templates/icinga-host.conf.j2 sieht so aus:
object Host "{{ hostitem }}" {
import "generic-host"
check_command = "hostalive"
address = "{{ hostvars[hostitem]['icinga_addr'] }}"
address6 = "{{ hostvars[hostitem]['icinga_addr6'] }}"
vars.os = "Linux"
zone = "MASTERFQDN"
}
Es unterstützt nur Dual-Stack Hosts. IPv4- oder IPv6-only werde ich versuchen später einzubauen. Die Services aus templates/icinga-services.conf.j2 entsprechen meinem Default-Setup:
// This file has been created by Ansible
object Service "disk" {
import "generic-service
check_command = "disk"
host_name = "{{ hostitem }}"
}
object Service "load" {
import "generic-service"
check_command = "load"
host_name = "{{ hostitem }}"
}
object Service "procs" {
import "generic-service"
check_command = "procs"
host_name = "{{ hostitem }}"
}
object Service "swap" {
import "generic-service"
check_command = "swap"
host_name = "{{ hostitem }}"
}
object Service "users" {
import "generic-service"
check_command = "users"
host_name = "{{ hostitem }}"
}
object Service "apt" {
import "generic-service"
check_command = "apt"
host_name = "{{ hostitem }}"
}
object Service "icinga" {
import "generic-service"
check_command = "icinga"
host_name = "{{ hostitem }}"
}
Die Services können später generischer vorkonfiguriert werden, wenn ich meine Ansible Hosts mit den entsprechenden Rollen versehen habe. Danach kann der Master die Konfiguration neu laden.
- name: Reload Icinga master configuration
shell: /etc/init.d/icinga2 reload
Für jeden Host legt man noch einen Eintrag im Ansible hosts File an, der die im Playbook verwedeten Parameter setzt:
[icinga2test]
HOSTNAME ansible_user=root icinga_distri=ubuntu icinga_release=trusty icinga_addr=IPv4 icinga_addr6=IPv6
icinga_distri kann auf debian oder ubuntu gsetzt werden, das icinga_release ist jeweils der Name der Distribution in den Paketquellen, also jessie, trusty, xenial usw. Es ist alles möglich, was im Icinga Repository gepflegt wird. Es sollte natürlich zur auf der VM installierten Distribution passen.
Ist soweit alles eingerichtet, kann das Playbook wie folgt verwendet werden:
ansible-playbook deploy-icinga-agent.yml -e hostitem=HOSTNAME
Die erneute Ausführung ist wie erwähnt möglich, auf dem Master geänderte Host oder Service-Konfiguration wird nicht überschrieben.
Im folgenden kann man alle genannten Files anschauen und bei Bedarf herunterladen. Bei der Verwendung muss noch darauf geachtet werden jeweils MASTERFQDN durch den Icinga2 Master zu ersetzen.