Ich arbeite nicht mit Git – Git arbeitet für mich

Immer wenn ich daran denke, was für ein praktisches Werkzeug Git doch ist, setzt es noch einen drauf. Als ob mir git bisect nicht schon genug Arbeit abnehmen würde, bin ich jetzt auch noch über git rerere gestolpert.

Was ist git rerere?

git rerere ist standardmäßig inaktiv und kann mit folgendem Befehl aktiviert werden:

$ git config --global rerere.enabled true

Sobald das getan ist, zeichnet Git auf, wie der Entwickler Zusammenführungs-Konflikte auflöst und verwendet diese Aufzeichnungen ggf. wieder.

Erklärung am Beispiel

Wir beginnen mit einem leeren Repository, das wir mit ein paar Commits füllen:

$ mkdir rerere-demo && cd $_
$ git init
Initialisierte leeres Git-Repository in /home/aklimov/rerere-demo/.git/
$ echo A >Buchstabensalat.txt
$ git add Buchstabensalat.txt
$ git commit -m A
[master (Basis-Commit) 4d1a53f] A
 1 file changed, 1 insertion(+)
 create mode 100644 Buchstabensalat.txt
$ echo B >>Buchstabensalat.txt
$ git commit -am B
[master fe71b2b] B
 1 file changed, 1 insertion(+)
$ git checkout HEAD^
$ echo C >>Buchstabensalat.txt
$ git commit -am C
[losgelöster HEAD 181ad2e] C
 1 file changed, 1 insertion(+)
$ git checkout -

Nun sollten wir so einen Baum vorfinden:

$ git --no-pager log --oneline --graph --decorate=full master 181ad2e
* 181ad2e C
| * fe71b2b (HEAD -> refs/heads/master) B
|/
* 4d1a53f A

Jetzt versuchen wir, die Commits B und C zusammenzuführen, was natürlich erfolgreich fehlschlagen wird. Wir müssen also den Konflikt manuell auflösen:

$ git merge 181ad2e
automatischer Merge von Buchstabensalat.txt
KONFLIKT (Inhalt): Merge-Konflikt in Buchstabensalat.txt
Recorded preimage for 'Buchstabensalat.txt'
Automatischer Merge fehlgeschlagen; beheben Sie die Konflikte und committen Sie dann das Ergebnis.
$ git --no-pager diff
diff --cc Buchstabensalat.txt
index 35d242b,8ec30d8..0000000
--- a/Buchstabensalat.txt
+++ b/Buchstabensalat.txt
@@@ -1,2 -1,2 +1,6 @@@
  A
++<<<<<<< HEAD
 +B
++=======
+ C
++>>>>>>> 181ad2e
$ git --no-pager diff
diff --cc Buchstabensalat.txt
index 35d242b,8ec30d8..0000000
--- a/Buchstabensalat.txt
+++ b/Buchstabensalat.txt
@@@ -1,2 -1,2 +1,3 @@@
  A
 +B
+ C
$ git add Buchstabensalat.txt
$ git commit --no-edit
Recorded resolution for 'Buchstabensalat.txt'.
[master 279715c] Merge commit '181ad2e'
$ git --no-pager log --oneline --graph --decorate=full
*   279715c (HEAD -> refs/heads/master) Merge commit '181ad2e'
|\
| * 181ad2e C
* | fe71b2b B
|/
* 4d1a53f A

Die Wirkung von git rerere macht sich bereits bei den rot hervorgehobenen Zeilen bemerkbar. An diesen Stellen wird aufgezeichnet, wie die Konflikt-Datei vorher bzw. nachher ausgesehen hat. Um zu sehen, was uns das bringt, verwerfen wir den Merge-Commit und führen erneut zusammen:

$ git reset --hard HEAD^
HEAD ist jetzt bei fe71b2b B
$ git --no-pager log --oneline --graph --decorate=full master 181ad2e
* 181ad2e C
| * fe71b2b (HEAD -> refs/heads/master) B
|/
* 4d1a53f A
$ git merge 181ad2e
automatischer Merge von Buchstabensalat.txt
KONFLIKT (Inhalt): Merge-Konflikt in Buchstabensalat.txt
Resolved 'Buchstabensalat.txt' using previous resolution.
Automatischer Merge fehlgeschlagen; beheben Sie die Konflikte und committen Sie dann das Ergebnis.
$ git --no-pager diff
diff --cc Buchstabensalat.txt
index 35d242b,8ec30d8..0000000
--- a/Buchstabensalat.txt
+++ b/Buchstabensalat.txt
@@@ -1,2 -1,2 +1,3 @@@
  A
 +B
+ C
$ git add Buchstabensalat.txt
$ git commit --no-edit
[master cb5c96b] Merge commit '181ad2e'
$ git --no-pager log --oneline --graph --decorate=full
*   cb5c96b (HEAD -> refs/heads/master) Merge commit '181ad2e'
|\
| * 181ad2e C
* | fe71b2b B
|/
* 4d1a53f A

Wie wir sehen können, hat Git, unmittelbar nachdem es einen Konflikt entdeckt hat, diesen automatisch mit Hilfe der vorher gespeicherten Aufzeichnung aufgelöst.

Da geht noch was

Selbstverständlich ist die Funktionalität von git rerere nicht auf so einfache Fälle beschränkt. Nehmen wir einmal an, dass wir nicht alleine an diesem Repository arbeiten und ein Kollege bereits etwas in den master-Zweig hochgeladen hat:

$ git checkout fe71b2b
$ vi Buchstabensalat.txt
$ git commit -am D
[losgelöster HEAD 779312b] D
 1 file changed, 1 insertion(+)
$ git checkout -
$ git --no-pager log --oneline --graph --decorate=full master 779312b
* 779312b D
| *   cb5c96b (HEAD -> refs/heads/master) Merge commit '181ad2e'
| |\
|/ /
| * 181ad2e C
* | fe71b2b B
|/
* 4d1a53f A

Wir müssen also unseren Merge-Commit über D legen, bevor wir pushen können:

$ git rebase --preserve-merges 779312b
Resolved 'Buchstabensalat.txt' using previous resolution.
automatischer Merge von Buchstabensalat.txt
KONFLIKT (Inhalt): Merge-Konflikt in Buchstabensalat.txt
Automatischer Merge fehlgeschlagen; beheben Sie die Konflikte und committen Sie dann das Ergebnis.
Error redoing merge cb5c96bcb52306dfbdcc8c728c6d356efa3ed9da
$ git --no-pager diff
diff --cc Buchstabensalat.txt
index f5a7bda,8ec30d8..0000000
--- a/Buchstabensalat.txt
+++ b/Buchstabensalat.txt
@@@ -1,3 -1,2 +1,4 @@@
 +D
  A
 +B
+ C
$ git add Buchstabensalat.txt
$ git rebase --continue
[losgelöster HEAD 5096395] Merge commit '181ad2e'
Successfully rebased and updated refs/heads/master.
$ git --no-pager log --oneline --graph --decorate=full
*   5096395 (HEAD -> refs/heads/master) Merge commit '181ad2e'
|\
| * 181ad2e C
* | 779312b D
* | fe71b2b B
|/
* 4d1a53f A

Fazit

Ich kann nicht für alle erdenklichen Teams sprechen, aber wir bei Icinga Web 2 werden uns jedenfalls nicht mehr absprechen müssen, wer gerade Konflikte auflöst und wer pushen darf. 🙂

Alexander Klimov

Autor: Alexander Klimov

Alexander hat Ende 2013 mit einem Praktikum bei NETWAYS gestartet. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der Arbeit an Icinga Web 2 und seiner Ausbildung bei uns friedliche Wege.

Jetzt wird der lahmen Schlange der Prozess gemacht

Python poweredEiner meiner Kollegen ist in diesem Beitrag bereits auf Python-betriebene Web-Server (WSGI) eingegangen. Ein mit make_server() erstellter Standard-WSGI-Server kann zur selben Zeit leider nur eine Anfrage bearbeiten, da weder Multithreading, noch Multiprocessing Verwendung finden. Mit dem Wissen wie es geht ist dies relativ einfach nachzurüsten und genau darauf will ich im folgenden eingehen.

Threads oder Prozesse?

Die Threadsicherheit von Python steht außer Frage. Die Umsetzung lässt allerdings zu wünschen übrig: Der Global Interpreter Lock verhindert gleichzeitiges Ausführen von Python-Bytecode in mehreren Threads. Somit sind Threads in Python mehr oder weniger für die Katz und es bleiben nur noch die Prozesse.

Diese können etwas umständlicher zu verwalten sein, bringen aber dafür zumindest einen wesentlichen Vorteil mit sich: Die einzelnen Prozesse sind voneinander komplett unabhängig.

Daraus folgt: Wenn ein Prozess abstürzt, bleibt der Rest davon unberührt. Daraus wiederum folgt: Wenn ein Prozess “amok läuft” kann er problemlos “abgeschossen” werden, um die System-Ressourcen nicht weiter zu belasten. (Im realen Leben undenkbar.)

Ein Server für alle Netzwerk-Schnittstellen

Wer weiß, dass seine Anwendung immer auf allen Netzwerk-Schnittstellen (oder nur auf einer) wird lauschen sollen, der hat ungleich weniger Aufwand bei der Implementierung:

from socket import AF_INET
from SocketServer import ForkingMixIn
from wsgiref.simple_server import make_server, WSGIServer

class ForkingWSGIServer(ForkingMixIn, WSGIServer):
    address_family = AF_INET

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield 'Hallo Welt!'

if __name__ == '__main__':
    make_server('0.0.0.0', 8080, app, server_class=ForkingWSGIServer).serve_forever()

 

Erklärung

Nach der Python-üblichen Unmenge an Importen wird die Klasse ForkingWSGIServer definiert – ein WSGIServer kombiniert mit dem ForkingMixIn. Dies funktioniert einwandfrei, da WSGIServer von BaseHTTPServer.HTTPServer erbt und letztgenannter wiederum ein SocketServer.TCPServer ist. HTTPServer, WSGIServer und ForkingMixIn überschreiben völlig unterschiedliche Methoden und kommen sich somit nicht in die Quere.

Darauf folgt eine beispielhafte WSGI-Anwendung, die theoretisch durch alles erdenkliche ersetzt werden kann.

Schlussendlich wird ein ForkingWSGIServer erstellt und er wird angewiesen, für einen unbestimmten Zeitraum die beispielhafte WSGI-Anwendung auf Port 8080 anzubieten.

Arbeitsweise

Der Server wird für jede ankommende HTTP-Anfrage einen neuen Prozess starten und diesen sie bearbeiten lassen. Während dessen kann er bereits einen neuen Prozess für die nächste schon wartende Anfrage erzeugen, usw..

Den Server sauber herunterfahren

Wenn der oben beschriebene Server z.B. via Strg-C o.ä. beendet wird, werden alle aktiven Verbindungen abrupt abgebrochen. Wenn das unerwünscht ist, kann der Server wie folgt erweitert werden:

import os
import sys
from signal import signal, SIGTERM
if __name__ == '__main__':
    signal(SIGTERM, (lambda signum, frame: sys.exit()))
    
    try:
        make_server('0.0.0.0', 8080, app, server_class=ForkingWSGIServer).serve_forever()
    finally:
        try:
            while True:
                os.wait()
        except OSError:
            pass

 

Erklärung

Kurz vor dem Start des Servers wird ein Signal-Handler registriert. Dieser sorgt dafür, dass der Python-Interpreter sich sauber beendet (sys.exit()) wenn er ein TERM-Signal erhält.

Diese Art der Beendigung wird abgefangen, um vor dem tatsächlichen Stopp auf alle noch laufenden Kindprozesse zu warten.

Ergebnis

Strg-C funktioniert zwar immer noch nicht so wie es soll, aber dafür der kill-Befehl.

Ein Server pro Netzwerk-Schnittstelle

Obwohl die gerade beschriebene Implementation einfach und schnell zu bewerkstelligen ist, kommt es vor, dass eine Anwendung auf mehreren, aber nicht auf allen Netzwerk-Schnittstellen lauschen soll. Hierzu muss der Haupt-Prozess für jede Schnittstelle einen weiteren Prozess, den eigentlichen WSGI-Server, erstellen.

import os
import sys
from multiprocessing import Process
from signal import signal, SIGTERM
from socket import AF_INET, AF_INET6
from SocketServer import ForkingMixIn
from time import sleep
from wsgiref.simple_server import make_server, WSGIServer


def print_msg(prefix, msg):
    print >>sys.stderr, '<{0}> {1}'.format(prefix, msg)


class ForkingWSGIServer(ForkingMixIn, WSGIServer):
    address_family = AF_INET


class ForkingWSGI6Server(ForkingWSGIServer):
    address_family = AF_INET6


def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield 'Hallo Welt!'


def run_forking_server(host, port, address_family=AF_INET):
    pid = os.getpid()

    print_msg(pid, 'Serving on {0}:{1}..'.format(host, port))

    server_class = ForkingWSGI6Server if address_family == AF_INET6 else ForkingWSGIServer
    try:
        make_server(host, port, app, server_class=server_class).serve_forever()
    finally:
        print_msg(pid, 'Shutting down..')

        try:
            while True:
                os.wait()
        except OSError:
            pass

        print_msg(pid, 'Exiting..')


if __name__ == '__main__':
    signal(SIGTERM, (lambda signum, frame: sys.exit()))

    processes = []

    port = 8080

    for (address_family, host) in (
        (AF_INET, '127.0.0.1'), (AF_INET6, '::1')
    ):
        p = Process(target=run_forking_server, args=(host, port, address_family))
        p.daemon = False
        p.start()
        processes.append(p)

    prefix = '{0} (root)'.format(os.getpid())

    print_msg(prefix, 'Waiting for SIGTERM..')
    try:
        while True:
            sleep(86400)
    finally:
        print_msg(prefix, 'Shutting down..')

        for p in processes:
            print_msg(prefix, 'Terminating {0}..'.format(p.pid))
            p.terminate()

        for p in processes:
            print_msg(prefix, 'Joining {0}..'.format(p.pid))
            p.join()

        print_msg(prefix, 'Exiting..')

 

Erklärung

Nach Registrierung des uns schon bekannten Signal-Handlers wird für jede Schnittstelle ein Prozess erstellt. Danach legt sich der Haupt-Prozess schlafen und wartet darauf, von einem SIGTERM aufgeweckt zu werden. Wenn das eintritt, terminiert er alle seine Kindprozesse und wartet, bis sie sich beendet haben.

Ein jeder dieser Kindprozesse startet wie gehabt einen WSGI-Server und wartet auf die Beendigung seiner Kindprozesse wenn ein SIGTERM eintrifft.

Fazit

Der vorgestellte Code ist rein demonstrativer Natur und entsprechend minimalistisch aufgebaut. Der Ursprung aller Dinge ist klein und dementsprechend ist noch viel Luft nach oben. Viel Spaß beim ausprobieren!

Vorausgesetzte Python-Version: 2.6 oder 2.7

Alexander Klimov

Autor: Alexander Klimov

Alexander hat Ende 2013 mit einem Praktikum bei NETWAYS gestartet. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der Arbeit an Icinga Web 2 und seiner Ausbildung bei uns friedliche Wege.

Azubis erzählen: September 2015 Alexander

This entry is part 10 of 13 in the series Azubis erzählen

Name: Alexander Klimov
Ausbildungsberuf: Fachinformatiker für Anwendungsentwicklung
Abteilung: Development
Lehrjahr: 2

C hat keine DLLs, sondern nur Headers”, sagte einst einer meiner ehemaligen Berufsschulklassenkollegen.

Solch grob fahrlässige fachbezogene Aussagen gebe ich nicht von mir – zumindest seit ich bei NETWAYS ausgebildet werde. Das Gegenteil des obigen Zitats habe ich hier nicht gelernt, dafür aber einige andere sehr interessante Sachen, die ich diesmal vorstellen will.

Git

Wer von den Fachinformatiker-Auszubildenden kennt das nicht: In der Berufsschule wird im Fach Anwendungsentwicklung und Programmierung ein Programm schrittweise weiterentwickelt und es wird einem nahe gelegt, vor jeder größeren Erweiterung des Programms den Quellcode zu sichern, um ggf. auf einen älteren Stand zugreifen zu können. Für diesen Zweck ist die Versionskontrolle Git viel besser geeignet – nicht zuletzt auch weil das Pflegen von vielen Versionen viel “attraktiver” ist. Ich hatte zu meinem Glück immer meinen Laptop dabei und kaum ein Programm, dessen Entwicklung nicht auf eine Schulstunde beschränkt war, ohne Git entwickelt.

$ mkdir AWP/Parkautomat && cd $_
$ git init
$ editor parkautomat.cpp && git add $_
$ git commit -m "Implementiere einen übertrieben übertriebenen Parkautomaten"
$ editor parkautomat.cpp && git add $_
$ git commit -m "Statte den Parkautomaten mit Funktionalität, die kein Mensch braucht, aus"

Zuletzt hat mich ein Kollege das Bisect-Kommando gelehrt. Dieses Wissen habe ich natürlich guten Willens bei nächstbester Gelegenheit weitergegeben.

Vim

Als ich bei NETWAYS angefangen habe, habe ich ernsthaft noch den Texteditor Nano verwendet – mit Ausreden wie “er genügt meinen Ansprüchen”. Aber kaum dass mir ein schönes neues Werkzeug in die Hände fällt, lasse ich den alten, primitiven Kram hinter mir. Auch in der Berufsschule hat der Editor gute Dienste geleistet. Es fiel mir viel leichter, damit zu arbeiten, als mit dem an der Schule propagierten Visual Studio. (Da liest mein Banknachbar in der 1. AWP-Stunde die 5-10-seitige bilderreiche Einführung in das letztgenannte Programm, während ich das zu schreibende Hallo-Welt-Programm schon längst mit Vim abgetippt und mit GCC kompiliert habe. Und – wer hätte das gedacht 😉 – es funktionierte!) Auch ich gehöre mittlerweile zu den Programmierern, die an ihrer .vimrc hängen.

$ cat ~/.vimrc
set bg=dark nu mouse=a ai et sts=4
sy on
nmap <F4> :qa<CR>
language de_DE.utf8
highlight Normal guibg=Black guifg=White

Puppet

… ist aus einer komplexen und automatisierten IT-Welt wie unserer nicht wegzudenken. Ob viele gleichartige Systeme aufwandarm aufgesetzt werden müssen oder die Entwicklungsumgebungs-VM eines Projektes mit einer Komanndozeile einsatzbereit sein muss – Puppet ist des Systemadministrators Freund und Helfer. Alleine die verglichen mit der konventionellen Bash verkürzte, intuitive und anti-redundante schreibweise für häufig verwendete Komponenten machen die Sprache attraktiv. Die Möglichkeit, Abhängigkeiten der Komponenten untereinander explizit zu definieren, nötigt bei komplexeren Beschaffungen der Systeme regelrecht zur Nutzung von Puppet. Im Rahmen eines Umbaus von Icinga Web 2 habe ich den Aufbau und die Funktionsweise von Puppet kennen gelernt. Mit Hilfe dieses Wissens habe ich für das jüngste von mir betreute Projekt von Grund auf Puppet-Manifeste geschrieben, um mir und anderen die Entwicklung zu erleichtern. Folgendes Beispiel installiert den Apache-Webserver auf Debian/Ubuntu, Fedora/RHEL, openSUSE/SLES und darauf basierenden Systemen:

$apache = $osfamily ? {
  'Debian' => 'apache2',
  'Suse'   => 'apache2',
  'RedHat' => 'httpd',
}
package { $apache:
  ensure => latest,
}
-> service { $apache:
  ensure => running,
  enable => true,
}

SQL

Angesichts des hohen Datenaufkommens, das Anwendungen heutzutage zu bewältigen haben, sind relationale Datenbanken unverzichtbar. Die Vielzahl unterschiedlicher Datenbankmanagementsysteme fordert einen möglichst großen gemeinsamen Nenner der Abfragesprachen. Dieser wurde mit SQL 1986 geschaffen und ist – nicht nur meiner Meinung nach – absolute Pflichtlektüre eines jeden werdenden Fachinformatikers. Im Rahmen der Entwicklung einer Stammdatenverwaltungssoftware bin ich dieser interessanten Thematik gelehrt worden – und auch in der Berufsschule soll das noch auf mich zukommen. Einer der größten Nutzen relationaler Datenbanken ist die Möglichkeit, Datensätze nur ein mal speichern zu müssen und mehrfach darauf verweisen zu können. Folgendes Beispiel ist eine Abfrage der Klassen und -leitungen von Schülern:

SELECT sn.name, k.name, ln.name
FROM schueler AS s
INNER JOIN klasse AS k ON k.id=s.klasse_id
INNER JOIN lehrer AS l ON l.id=k.lehrer_id
INNER JOIN natuerliche_person AS sn ON sn.id=s.natuerliche_person_id
INNER JOIN natuerliche_person AS ln ON ln.id=l.natuerliche_person_id;

Die Personalien von Schülern und Lehrern und evtl. anderen natürlichen Personen werden hierbei – da gleichartige Datensätze – in einer Tabelle gespeichert. Da bspw. ein Lehrer mehrere Klassenleitungen haben kann, wird er nur ein mal gespeichert und über lehrer.id referenziert, um Redundanz zu vermeiden.

Fazit

Lehrjahre sind zwar keine Herrenjahre, können aber eine große Bereicherung sein – vor allem für die kommenden Herrenjahre. Und mein vergangenes Lehrjahr hat gezeigt: Sich reinzuhängen lohnt sich – auch bei den unbeliebten Berufsschulfächern.

Alexander Klimov

Autor: Alexander Klimov

Alexander hat Ende 2013 mit einem Praktikum bei NETWAYS gestartet. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der Arbeit an Icinga Web 2 und seiner Ausbildung bei uns friedliche Wege.

Eine Taste für alle Fälle

Trotz der Stabilität von GNU/Linux kann es vor allem bei Vorabversionen u. U. vorkommen, dass sich das System aufhängt und nicht mehr auf Eingaben reagiert. Wer auf diesen Fall vorbereitet ist, muss im Zweifelsfall nicht gleich den Stecker ziehen und evtl. wertvolle Daten verlieren.

Magische S-Abf-Taste

Die Magische S-Abf-Taste ermöglicht es Benutzern, die vor einer physischen Tastatur sitzen, dem Kernel direkt Befehle zu erteilen. Je nach Distribution/Standard-Konfiguration muss dieser Kanal zuerst überhaupt freigeschaltet werden:

# sysctl kernel.sysrq=502

Zwecks Persistenz muss der Datei /etc/sysctl.conf eine entsprechende Zeile hinzugefügt (bzw. angepasst) werden:

kernel.sysrq=502

Nun ist es an der Zeit, die Taste zu testen. Dazu kann – vorzugsweise in einer Konsole (Strg+Alt+F2) – die Tastenkombination Alt+SysAbf+H, die die Hilfe anzeigen lässt, verwendet werden (Alt drücken und halten, SysAbf drücken und loslassen, H drücken und loslassen, Alt loslassen). Im Erfolgsfall kann zukünftig bspw. ein nicht mehr reagierender X-Server mit Alt+SysAbf+K abgeschossen werden.

Laptops und Netbooks

… haben meist eine kompakte(re) Tastatur im Vergleich zu Schreibtischrechnern mit weniger Tasten und evtl. keiner SysAbf-Taste. Eine vorhandene Druck-Taste muss nicht zwingend auch die SysAbf-Taste stellen. Um deren Funktionalität trotzdem nutzen zu können, muss eine andere Taste “umgebogen” werden. Hierzu wird in einer Konsole showkey -s eingegeben. Dieses Kommando müsste folgende Ausgabe erzeugen:

KB-Modus war UNICODE
[ Wenn Sie das unter X probieren, muss es nicht funktionieren, 
 da der X Server ebenfalls von /dev/console liest. ]

Drücken Sie eine Taste (Programmende 10 s nach dem letzten Tastendruck)...
0x9c

Nun muss die umzubiegende Taste – bspw. die Windows-Taste – gedrückt und losgelassen werden. Dies sollte folgende Ausgabe erzeugen:

0xe0 0x5b 0xe0 0xdb

Von diesen 4 Hexadezimalzahlen steht die erste Hälfte (0xe05b) für das Drücken und die zweite für das Loslassen der Taste. Nun kann die Windows-Taste mit folgendem Kommando umgebogen werden:

# setkeycodes e05b 99

Die 99 steht hierbei für die SysAbf-Taste.

$ grep -Fwe KEY_SYSRQ /usr/include/linux/input.h
#define KEY_SYSRQ        99
$

Zwecks Persistenz muss dieses Kommando automatisch beim Systemstart ausgeführt werden. Dies kann z. B. wie folgt eingerichtet werden:

# echo "
# Make the Windows key working like SysRq
@reboot root $(which setkeycodes) e05b 99" >>/etc/crontab

Fazit

Die meisten häufig genutzten Distributionen haben alles nötige schon von Haus aus eingerichtet. Meiner Meinung nach ist diese Anleitung trotzdem zumindest für Nutzer von Systemen wie Arch, Fedora oder Debian Testing absolutes Pflichtprogramm. Möglichen Datenverlusten einen Riegel vorzuschieben kann keinesfalls schaden.

Alexander Klimov

Autor: Alexander Klimov

Alexander hat Ende 2013 mit einem Praktikum bei NETWAYS gestartet. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der Arbeit an Icinga Web 2 und seiner Ausbildung bei uns friedliche Wege.

Reverse proxying vom Feinsten

Kibana stellt seit Version 4 den Web-Server selbst. Dieser fungiert auch als (transparenter) Proxy zwischen dem Klienten und Elasticsearch. Dennoch scheint kaum einer (der Entwickler), sich Gedanken um die Sicherheit gemacht zu haben. Hinz und Kunz werden beim Versuch, auf Kibana zuzugreifen, von nichts und niemandem (außer vielleicht der Firewall) aufgehalten. Das ist – vor allem in (größeren) Produktionsumgebungen – nicht unbedingt die wünschenswerteste Situation. Dennoch muss deswegen noch lange niemand gleich Kibana patchen (oder sich vor den nächsten Zug werfen). Diese Lücke kann auch ein Reverse Proxy schließen.

Was ist ein Reverse Proxy?

Während ein konventioneller Proxy einem Klienten die Verbindung zu n Diensten ermöglicht – also für den Klienten von ihm gestellte Anfragen an Dienste weiter reicht – macht ein Reverse Proxy das Gegenteil. Er nimmt von n Klienten Anfragen, die für einen Dienst bestimmt sind, entgegen (und reicht diese entsprechend weiter).

Anwendungsbeispiele:

  • Cache
  • Lastverteiler
  • Honeypot
  • (zusätzliche) Firewall

Abschirmung von Kibana

Der Apache HTTPd kann bspw. als Reverse Proxy dienen und muss dafür wie folgt konfiguriert werden:

ProxyPass / http://127.0.0.1:5601/
ProxyPassReverse / http://127.0.0.1:5601/

Dadurch wird Apache angewiesen, alle (`/‘) Anfragen an Kibana weiterzuleiten und die Antworten entsprechend an die Klienten zurück zu senden.

Natürlich ist das an und für sich noch keine Abschirmung. Allerdings kann Apache (ganz einfach) mit Passwort-Authentifizierung, SSL u.v.m. ausgestattet werden.

Beispielkonfiguration:

<VirtualHost *:443>
    SSLEngine on
    SSLCertificateFile /etc/pki/tls/certs/localhost.crt
    SSLCertificateKeyFile /etc/pki/tls/private/localhost.key

    ProxyPass / http://127.0.0.1:5601/
    ProxyPassReverse / http://127.0.0.1:5601/

    <Location />
        AuthType Basic
        AuthName "Top secret"
        AuthBasicProvider file
        AuthUserFile /etc/httpd/passwd
        Require valid-user
    </Location>
</VirtualHost>

Und schon können wir etwas ruhiger schlafen. 🙂

Alexander Klimov

Autor: Alexander Klimov

Alexander hat Ende 2013 mit einem Praktikum bei NETWAYS gestartet. Als leidenschaftlicher Programmierer und begeisterter Anhänger der Idee freier Software, hat er sich dabei innerhalb kürzester Zeit in die Herzen seiner Kollegen im Development geschlichen. Wäre nicht ausgerechnet Gandhi sein Vorbild, würde er von dort aus daran arbeiten, seinen geheimen Plan, erst die Abteilung und dann die Weltherrschaft an sich zu reißen, zu realisieren - tut er aber nicht. Stattdessen beschreitet er mit der Arbeit an Icinga Web 2 und seiner Ausbildung bei uns friedliche Wege.