Store passwords like a boss #2

Als Programmierer von Webanwendungen, die Ihre Benutzer selbst verwalten, kommt man um das Thema Sicherheit nicht herum – so auch ich als Mitentwickler von Icinga Web 2.

Aus diesem Anlass habe ich recherchiert, wie Passwörter von Benutzern sinnvoll gespeichert werden können und mein neu gewonnenes Wissen geteilt.

Darüber hinaus habe ich vorgeschlagen, Icinga Web 2 dementsprechend zu erweitern. Eric hat diesen Vorschlag begutachtet und mich auf eine Alternative aufmerksam gemacht, nämlich…

password_hash()

PHP bietet seit Version 5.5 u. a. diese Funktion, um Passwörter zu verschlüsseln. Diese ersetzt meine selbst geschriebene Funktion aus dem Vorgänger-Blogpost vollständig:

$encryptedNewPassword = password_hash($newPassword, PASSWORD_DEFAULT);

Der Rückgabe-Wert kann direkt in einer Datenbank gespeichert werden – so einfach ist das.

Der zweite Parameter der Funktion bestimmt den Verschlüsselungs-Algorithmus. Damit kann man diesen zwar frei wählen, aber ich empfehle, den Standard vorzuziehen, denn dieser ändert sich (laut Doku) mit neueren PHP-Versionen zu Gunsten der Sicherheit. Damit bleiben zumindest die neuen/geänderten Passwörter aktuell was die Sicherheit angeht.

password_verify()

Wie der Name schon andeutet, überprüft diese Funktion ein vom Benutzer eingegebenes Passwort auf Übereinstimmung mit einem verschlüsselten Passwort:

$ok = password_verify($enteredPassword, $encryptedPassword);
if ($ok) {
    ...

password_needs_rehash()

Wie bereits erläutert: mit einem neuen Standard-Verschlüsselungs-Algorithmus werden automatisch alle neu verschlüsselten Passwörter sicherer. Aber was ist mit den bereits bestehenden?

Da bestehende Passwörter (aus gutem Grund!) nur verschlüsselt vorliegen, können sie nicht alle “in einem Abwasch” migriert werden. Die einzige Gelegenheit ist die Anmeldung eines Benutzers, die das Klartext-Passwort erfordert. Und als wäre die Funktion für diese Gelegenheit geschrieben…

if ($ok) {
    $outdated = password_needs_rehash($encryptedPassword, PASSWORD_DEFAULT);
    if ($outdated) {
        $encryptedPassword = password_hash($enteredPassword, PASSWORD_DEFAULT);
    }
    ...

Fazit

Der IT-Branche wird nicht umsonst nachgesagt, dass sie eine “permanente Innovation” vollbringe. Umso wichtiger ist es, aus Gründen der Datensicherheit, mit der dunklen Seite der Macht mitzuhalten.

Denn die sind böse, sehr böse…

Alexander Klimov

Autor: Alexander Klimov

Alexander hat 2017 seine Ausbildung zum Developer bei NETWAYS erfolgreich abgeschlossen. 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 bei uns friedliche Wege.

PHP Error – php_network_getaddresses

Ein Problem zu dem es viele Lösungsansätze gibt, ist folgender PHP Fehler. Er tritt beispielsweise auf, beim Verbinden auf externe Anwendungen oder APIs:
php_network_getaddresses: getaddrinfo failed: No address associated with hostname

Im Netz kursieren Teilweise die wildesten Lösungsansätze. Da ich vor kurzem mit eben diesem Problem konfrontiert wurde, möchte ich meine Erfahrung mit euch teilen. In der “php.ini” gibt es einen Parameter, der diesen Fehler verursachen kann.

Dieser kann unterschiedlich gefüllt sein und ist per default leer.

disable_functions = pcntl_alarm,pcntl_fork,,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,,pcntl_signal,,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,,pcntl_exec,pcntl_getpriority,pcntl_setpriority

Um obigen Fehler zu beheben, kann es also reichen, sich diesen Parameter anzusehen und notfalls sogar komplett zu leeren.

Marius Gebert

Autor:

Marius ist seit September 2013 bei uns beschäftigt. Er hat im Sommer 2016 seine Ausbildung zum Fachinformatiker für Systemintegration absolviert und kümmert sich nun um den Support unserer Hostingkunden. Seine besonderen Themengebiete erstrecken sich vom Elastic-Stack bis hin zu Puppet. Auch an unserem Lunchshop ist er ständig zu Gange und versorgt die ganze Firma mit kulinarischen Köstlichkeiten. Seine Freizeit verbringt Marius gern an der frischen Luft und wandert dabei durch die fränkische Schweiz

Warum veranschaulichen wir?

Jedem fällt wohl auf die Schnelle ein Beispiel ein, wo er in seinem Leben schon mal etwas veranschaulicht hat, ob nun für sich Selbst oder für Andere:

  • Das fängt schon in der Schule an; man hält ein Referat über ein kompliziertes Thema und muss dieses seinen Klassenkameraden verständlich erläutern.
  • Man arbeitet in der Uni an einem Text mit einer schier unermessbarer Tiefe und muss sich selbst veranschaulichen, was da nun eigentlich vorgeht.
  • Oder man ist auf der Arbeit in einer Position in der man neue Kollegen in ein Thema einführen muss, um diese nicht mit der Fülle an Informationen zu erschlagen, zieht man zum Vergleich einen ähnlichen, bereits bekannten Ablauf her.

In meinem praktischen Beispiel habe ich das neue Projekt, dass mein Mitazubi und ich nun zu bearbeiten haben.

Wir schreiben im Moment einen “Parser”, der für uns Informationen aus Übersetzungsdateien von Icinga Web 2 auslesen und in eine sortierte Array-Struktur packen soll.

Diese sind in dem .po Format von Poedit strukturiert.

Das sieht ungefähr so aus:

#: /this/is/the/location/toTranslate.php
#| msgid “tranlsate this”

msgid “Translate this.”
“This too, please”
msgtxt “Übersetze dies.”
“Das auch, bitte”

Diese Datei wird nun sortiert nach den verschiedenen Zeilenanfängen, die bereits anzeigen was der sich dahinter befindende String aussagt.

Die Veranschaulichung zur Lösung ist bei uns eine Art Wasserfall/Trichtersystem:
Die Zeile #| msgid “tranlsate this” würde wie folgt sortiert werden

blogpic

 

Im Code haben wir das mit einer sehr verschachtelten Konstruktion mit einigen Switch-Cases gelöst.
(Und dieses Chaos auch schon längst Grundüberholt)

switches

Dies zeigt auch, dass die Visualisierungen die wir machen um uns das Konzept darzustellen teilweise von der Umsetzung her stark abweichen. Solange es uns jedoch hilft die Übersicht zu bewahren, ist eine visuelle Darstellung immer gut.

In den meisten Fällen führt das Programmieren ohne visuelle Umsetzung dazu, dass der Code ungeordnet ist und man viele Fälle übersieht, die man nachher durch “dirty” Fixes wieder zu richten versucht. Das macht den Code unleserlich und führt dazu, dass die gesamte Arbeit umsonst war, da man den Code komplett neu strukturieren und neu schreiben muss.

Dies zeigt sich auch an unserem Projekt. Wir arbeiten gerade an der zweiten Version des Parsers, da wir das Problem in der ersten Version relativ ungeordnet angegangen sind, da wir fälschlicherweise angenommen hatten, dass so ein “kleines” Progrämmchen keiner größeren Planung bedarf. Oh wie wir uns geirrt haben.

Die jetzige Planung hat sich etwas gedehnt…

parser

Also daran denken: erst planen, dann schreiben.

Jennifer Mourek

Autor: Jennifer Mourek

Jennifer hat sich nach Ihrem Abitur erst einmal die große weite Welt angesehen, ehe es sie für Ihre Ausbildung zur Fachinformatikerin für Anwendungsentwicklung ab September 2016 nach Nürnberg zu NETWAYS verschlagen hat. Die Lieblingsfreizeitaktivitäten der gebürtigen Steigerwalderin sind das Zocken, Bouldern und sich mit Freunden und Kollegen auf gemütliche Spiele- und Filmabende zu treffen.

Icinga Director 1.2.0 veröffentlicht

Gerade mal 4 Monate sind seit eploer 1.1.0 vergangen, und wenig mehr als 7 Monate seit unserer ersten stabilen Release. Der Icinga Director reifte schnell, und aus einem kleinen Icinga Web 2 Modul ist mittlerweile ein ziemlich großes Projekt erwachsen. Gestern haben wir die Version 1.2.0 freigegeben, vollgestopft mit neuen Features, Bugfixes und Usability-Verbesserungen.

Berechtigungen

Dies ist die erste Release mit offizieller Unterstützung für Berechtigungen. Die Aufgabe eines Icinga-Administrator ist es, Commands, Service- und Host-Vorlagen zu definieren und eventuell alle Schritte von Import/Sync bis zum Deployment der Konfiguration zu automatisieren. Hier setzt der Director an, er ist zuallererst ein mächtiges Automatisierungstool.

Viele wenn nicht gar die meisten Umgebungen haben aber dennoch immer noch so einige Aufgaben die sich schwer automatisieren lassen. Wer kennt nicht den Mitarbeiter der sich zweimal am Tag umentscheidet, ob den nun der Schwellwert für seine seit Monaten volle Platte bei 99,9 oder doch besser bei 99,8% liegen soll. Stehen gerade 3000 neue Router an, die (nachdem jeweils ein Ticket erstellt wurde) im Laufe der nächsten Monate überwacht werden sollen? Diese Aufgaben sind prädestiniert dafür, an andere delegiert zu werden!

Audit log

Konfigurationsvorschau, Core API inspection oder das Auditlog können sensible Informationen beinhalten. Solcherlei Berechtigungen gibt man also besser nur denen, die sie wirklich brauchen. Soll ein Auditor alle Änderungen sehen aber keinen administrativen Zugang erhalten? Auch das ist jetzt möglich. Alle Aktivitäten lassen sich jetzt außerdem zusätzlich ins Syslog schreiben, für den Fall dass man es anderweitig archivieren möchte.

 

Performance

Das Rendern der Konfiguration wurde enorm beschleunigt. In großen Live-Umgebungen benötigt das Deployment nur noch ein Fünftel der ursprünglichen Zeit. Eine mir bekannte Umgebung konnte heute übrigens ihr tausendstes (automatisiertes) Director-basiertes Deployment feiern. Das Ganze bei mehr als einer halben Million Einzeländerungen im Aktivitätslog.

 

Konfigurationsmöglichkeiten

Es ist jetzt möglich tief verschachtelte Assign-Regeln zu definieren, basierend auf allen Host- und/oder Service-Eigenschaften. Wer das “apply for” Feature von Icinga 2 vermisst hat darf sich jetzt auch freuen. Ein Team von Internap hat einen Großteils des Codes dafür beigetragen. Bei dieser Gelegenheit herzlichen Dank für die gute Arbeit!

Die Unterstützung für Icinga 2 DSL in Command-Definitionen wurde verbessert. Ein Blick auf die Screenshots im Support-Ticket #12928 gibt einen kleinen Eindruck von den schrägen Möglichkeiten, die sich dadurch ergeben. Ein paar versteckte Features wie die implizite Unterstützung von skip_key fanden ebenfalls ihren Weg in die neue Version.

Nested filter with apply forDirector - Icinga DSL Cluster Check Override inherited service vars Apply-for - Preview

 

Wolltest Du immer schon eine bestimmte Eigenschaft eines von einem Host-Template geerbten Services für nur einen Host überschreiben? Dies und ähnliche Features sind jetzt verfügbar!
DNS - Property Modifier

Automatisierung

Beim Arbeiten mit der Import/Sync-Funktionalität lassen sich viele kleine Verbesserungen entdecken. Es gibt neue “Property-Modifier”, und deren Ergebnis lässt sich jetzt auch dafür verwenden neue virtuelle Eigenschaften zu erstellen. Wer mit Datenfeldern arbeitet wird neben Usability-Verbesserungen die Möglichkeit entdecken, andere Objekte in einer Dropdown-Liste bereitzustellen und als String oder Array in Custom-Variablen zu nutzen.

 

Testing

Immer mehr Entwickler tragen Quellcode zum Icinga Director bei. Um den Einstieg zu erleichtern haben wir das Bootstrapping unserer Unit-Tests vereinfacht und entsprechende Dokumentation bereitgestellt.

 

GUI und Usability

Die Fehlerbehandlung in den Formularen wurde stark verbessert, eventuelle Exceptions werden an vielen Stellen gefangen und auf lesbare Weise dargestellt. Der Deployment-Button ist jetzt nicht mehr so versteckt, die Konfigurationsvorschau wurde verbessert und erlaubt jetzt vollständige Konfigurations-Diffs auch vor dem Ausrollen einer neuen Konfiguration.

Loop detection Multiedit - imports Confirm field removal

 

Loops bei der Vererbung werden jetzt freundlich dargestellt und lassen sich direkt im Frontend beheben. Ein verstecktes Juwel ist die “Multi-Edit”-Funktionalität. Mit SHIFT/ALT+Klick lassen sich mehrere Hosts auswählen. Deren Imports, Custom-Variablen und andere Eigenschaften lassen sich an dieser Stelle für alle ausgewählten Objekte auf einmal ändern. Fehler oder Warnungen in allen historischen Startup-Logs verlinken jetzt direkt zur entsprechenden Konfigurationsdatei und springen an die richtige Zeile.

 

Verwandte Module

Es gibt mehr und mehr zusätzliche Module welche vom Director bereitgestellte Hooks implementieren. AWS-Import für EC2-Instanzen, ELBs und Autoscaling-Gruppen. Datei-Import für CSV, JSON, YAML und XML. Wir hörten von verschiedenen erfolgreichen Import-Source-Implementierungen in unterschiedlichen Projekten und würden uns sehr freuen, noch mehr davon als freie Software bereitgestellt entdecken zu dürfen!

 

Import from AWS Fileshipper - Import files

 

Download

Der Icinga Director v1.2.0 ist hier verfügbar, und das entsprechende GIT-Repository findet sich natürlich auf GitHub.

 

Thomas Gelf

Autor: Thomas Gelf

Der gebürtige Südtiroler Tom arbeitet als Principal Consultant für Systems Management bei NETWAYS und ist in der Regel immer auf Achse: Entweder vor Ort bei Kunden, als Trainer in unseren Schulungen oder privat beim Skifahren in seiner Heimatstadt Bozen. Neben Icinga und Nagios beschäftigt sich Tom vor allem mit Puppet.

GitLab – Webhooks

gitlab-webhooks

Da ich vor kurzem für einen unserer namhaften Kunden ein GitLab Setup aufsetzten durfte und dieser sich nun auch Automatische Checkouts seiner Live Branches auf seinen Produktiv Systemen wünschte, habe ich mich dazu entschlossen euch ein bisschen an der Einrichtung dieser Mechaniken teilhaben zu lassen, natürlich nicht im vollem Umfang des Projektes für unseren Kunden, das eine oder andere habe ich für diesen Artikel verständlicherweise leicht abwandeln müssen, das hier gezeigte funktioniert trotzdem, ich habe es selber ausprobiert.

Dann legen wir mal los…

In einem klassischen Git Setup werden die Hooks direkt im Repository im Unterordner hooks abgelegt, hier mal ein Beispiel wie das aussehen kann…

$ MyAwesomeProject/hooks $ ll
-rwxrwxr-x 1 enzo enzo  452 Jun 28 11:46 applypatch-msg.sample*
-rwxrwxr-x 1 enzo enzo  896 Jun 28 11:46 commit-msg.sample*
-rwxrwxr-x 1 enzo enzo  189 Jun 28 11:46 post-update.sample*
-rwxrwxr-x 1 enzo enzo  398 Jun 28 11:46 pre-applypatch.sample*
-rwxrwxr-x 1 enzo enzo 1642 Jun 28 11:46 pre-commit.sample*
-rwxrwxr-x 1 enzo enzo 1239 Jun 28 11:46 prepare-commit-msg.sample*
-rwxrwxr-x 1 enzo enzo 1352 Jun 28 11:46 pre-push.sample*
-rwxrwxr-x 1 enzo enzo 4898 Jun 28 11:46 pre-rebase.sample*
-rwxrwxr-x 1 enzo enzo 3611 Jun 28 11:46 update.sample*

GitLab geht hier allerdings einen anderen Weg, da dieses das hooks Verzeichnis durch einen Symbolischen Link auf System eigene Hooks umlenkt (wie hier Beispielhaft zu sehen ist)…

gitlab-webhooks-art2

…nun sollte man hier auch besser die Finger heraus lassen, da GitLab dieses Verzeichnis für allerlei anderen Mechaniken benötigt, der Weg über die Webhooks ist meiner Meinung nach aber auch flexibler (allein schon wegen der einfachen Anbindung an Web Dienste wie GitHub, Bitbucket, Heroku, etc.).

Nun direkt zum spaßigen Teil, im GitLab benötigen wir einen neuen User ohne irgendwelche besonderen Rechte incl. SSH Public Key, fangen wir mit dem Key selbst an, da der Checkout User keinen besonderen Rechte benötigt außer einen Pull/Fetch zu tätigen, reicht es uns den Key also entsprechend Unprivilegiert zu preparieren (bei der Passwortabfrage bitte nichts angeben, einfach mit Enter bestätigen)…

$ ssh-keygen -t rsa -b 2048 -O clear -O no-agent-forwarding -O no-port-forwarding -O no-pty -O no-user-rc -O no-x11-forwarding -f gitlab.key
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in gitlab.key.
Your public key has been saved in gitlab.key.pub.
The key fingerprint is:
4b:f3:ae:60:d4:5d:5e:02:75:64:4c:7f:ce:09:ec:8a enzo@ebony
The key's randomart image is:
+--[ RSA 2048]----+
|          ...+=  |
|           ..o.. |
|            oo. o|
|       . . o.o.oo|
|      . S . .. .o|
|     . . +. .    |
|      o .E..     |
|     . . .       |
|        ...      |
+-----------------+
$ ll
insgesamt 8
-rw------- 1 enzo enzo 1675 Jun 28 11:52 gitlab.key
-rw-r--r-- 1 enzo enzo  392 Jun 28 11:52 gitlab.key.pub

…nun benötigen wir lediglich noch einen User im GitLab, das sollte keine große Herausforderung darstellen, bitte stellt auch sicher das ihr den SSH PublicKey in unserem Beispiel gitlab.key.pub dem User, lasst ihn uns hier der Einfachheit halber checkout nennen zuweist, da die Webhooks mit den Berechtigungen des Standard Apache User www-data läuft (das ist zumindest unter Debian/Ubuntu der Fall, unter Redhat Systemen heißt dieser wwwrun) solltet ihr dem Key als Identifier so etwas wie www-data to gitlab oder ähnliches benennen, damit ihr später ganz einfach den Überblick behalten könnt…

gitlab-webhooks-art4
gitlab-webhooks-art5

…nun müssen wir unserem User checkout lediglich noch in unser Projekt aufnehmen, die Rechte eines Reporter sollten dafür ausreichend sein um Fetch/Pull oder auch Clone zu können, mehr benötigen wir an dieser Stelle nicht…

gitlab-webhooks-art9

…somit sind wir auf schon fast fertig, zumindest was das GitLab Setup selbst betrifft, nun müssen wir lediglich noch die URL unseres Webhook konfigurieren und schon können wir uns dem Hook selber widmen…

gitlab-webhooks-art7

gitlab-webhooks-art8…bitte beachtet auch, das es beim Entwickeln eines Webhooks sehr hilfreich sein kann wenn ihr über den Button [Test Hook] den Push Event einfach mal Manuell triggert, ansonsten würde euer Webhook erst aufgerufen werden wenn ihr wirklich in das Repository Pusht (das geht natürlich auch, legt euch hierzu einfach ein neues Repository zum spielen an).

So nun geht es an den Server der unseren Webhook ausführen soll, ihr benötigt lediglich einen simplen Apache/NginX vHost mit Standard Rechten, man kann selbstverständlich auch ein komplizierteres Deployment bauen aber darum geht es in diesem Artikel nicht (ich nutzte hier den Apache2 mit PHP5, da dieser bereits auf dem Kunden System vorhanden wahr und zusätzliche Scriptsprachen installieren ist meist schlechte Praxis sowie auch unnötiger Aufwand).

Ich habe mich hier für den Standard Pfad des Apache Setups entschieden da der Standard vHost bereits auf diesen zeigt und dieser auch nur aus dem internen Netz bedienbar ist, somit ist sichergestellt das Niemand von außerhalb Unfug treiben kann.

Dort habe ich im DocumentRoot des Apache ein Verzeichnis api erstellt, der volle Pfad lautet hier /var/www/html/api, dort habe ich mein Webhook mit Namen on-push.php abgelegt, dieser hat nun folgenden Inhalt…

<?php
    // global settings
    define( "DEBUG", true );
    define( "OK", "Status: 200" ); 
    define( "ERR", "Status: 503" );
    define( "LOGFILE", getcwd()."/on-push.log" );
 
    // project settings
    define( "PROJECTROOT", "/var/www/project1/htdocs" );
    define( "BRANCH", "staging" );
    define( "COMMAND", "/bin/bash -c \"git fetch --all && git reset --hard origin/".BRANCH."\"" );
 
    // user which can do/trigger some updates on our staging branch
    $USERS = array( 'operator', 'developer1', 'developer2' );
 
    // =================================== MAIN =============================================
 
    try { 
        if( DEBUG ) {
            if( file_exists( LOGFILE ) ) unlink( LOGFILE );
            msg( "====== SERVER VARIABLES BEGIN ======" );
            msg( print_r( $_SERVER, true ) ); 
            msg( "====== SERVER VARIABLES END ======" );
        } 
 
        if( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
            $obj = getRequestBody();
            msg( "====== JSON OBJ BEGIN ======" );
            msg( print_r( $obj, true ) );
            msg( "====== JSON OBJ END ======" );
 
            if( $obj->object_kind == 'push' ) {
                if( $obj->ref == 'refs/heads/'.BRANCH ) {
                    $allowed = false;
                    foreach( $USERS as $user ) {
                        msg( "permission test ( ".$obj->user_name ." == ". $user." ) ..." );
                        if( preg_match( "/$user/i", $obj->user_name ) ) {
                            msg( $user . " allowed to do updates on refs/heads/".BRANCH." branch" );
                            $allowed = true;
                            break;
                        }
                    } 
 
                    if( $allowed ) {
                        if( $obj->before != "0000000000000000000000000000000000000000" ) {
                            msg( "changing directory to ".PROJECTROOT );
                            chdir( PROJECTROOT );
 
                            msg( "executing: ". COMMAND );
                            $fh = popen( COMMAND." 2>&1", "r" );
                            $result = fread( $fh );
                            while( !feof( $fh ) ) {
                                msg( rtrim(fgets( $fh, 4096 )) );
                            } 
                            pclose( $fh );
                        } else {
                            msg( "it's a empty repository, we'll do nothing at this point" );
                        } 
                    } else {
                        msg( "permission denied for ". $obj->user_name );
                    }
                } else {
                    msg( "everybody can push to ". $obj->ref.", but we'll do nothing, please check your project settings" );
                }
            } else {
                msg( "[ ". $obj->object_kind ." ] event handler not yet implemented" );
            }
        } else {
            msg( strtoupper($_SERVER['REQUEST_METHOD'])." from [ ".$_SERVER['REMOTE_ADDR'] ." ] is not allowed, please check your server settings" );
            return_status( ERR );
        }
    } catch( Exception $e ) {
        msg( "======= EXCEPTION BEGIN ========" );
        msg( $e->getMessage() );
        msg( "======= EXCEPTION END ==========" );
        return_status( ERR );
    }
 
    return_status( OK ); 
 
    // ================================ FUNCTIONS ===========================================
    function return_status( $status = ERR ) { header( $status ); }
    function getRequestBody() { return json_decode( file_get_contents('php://input') ); }
    function msg( $message ) { if( $message != null ) file_put_contents( LOGFILE, $message."\n", FILE_APPEND ); } 
?>

..damit das nun funktionieren kann, müssen wir nun noch das Projekt Klonen und die SSH Settings anlegen, fangen wir daher direkt mit den SSH Settings an…

# (beginne mit Root Login)
su - -s /bin/bash www-data
mkdir -p .ssh && chmod 0600 .ssh
exit
cp gitlab.key /var/www/.ssh/ && chown www-data. /var/www/.ssh/gitlab.key && chmod 0400 /var/www/.ssh/gitlab.key
su - -s /bin/bash www-data
vi .ssh/config

…die .ssh/config sieht dabei wie folgt aus…

Host gitlab.example.org
    UserKnownHostsFile /dev/null
    StrictHostKeyChecking no
    IdentityFile ~/.ssh/gitlab.key
    User git
    LogLevel VERBOSE

…speichert nun das ganze und Klont eurer Projekt nach /var/www/project1/htdocs

# (beginne mit Root Login)
su - -s /bin/bash www-data
cd /var/www/project1
git clone git@gitlab.example.org:test/number1.git htdocs

…wenn ihr nun Lokal mit eurer Arbeitskopie arbeitet und an dem Punkt seit das ihr alles fertig zuhaben scheint und dann einen Push in euren staging Branch macht, wird GitLab jedesmal den Webhook on-push.php Aufrufen und auf dem Projekt/Web -Server einen Fetch/Pull ausführen lassen, um das Projekt auf den selben Stand wie eure Arbeitskopie zu bringen.

Zugegeben der PHP Handler oben ist keinesfalls perfekt, ich bin auch kein guter PHP Programmierer, genau genommen eigentlich gar keiner ;). Der Hook für das Kunden Projekt umfasst zudem noch mehr Fähigkeiten und würde mit seine zu diesem Zeitpunt ca. 800 Zeilen diesen Artikel in jedem Fall sprengen, aber ich denke das der gezeigte Webhook oben bereits eine gute Ausgangsbasis darstellt, sodass ich euch hoffentlich dazu Animieren konnte, das ganze doch einmal selber nachzubauen.

Zum zweck des Debugging, loggt dieser zur Kontrolle nach /var/www/html/api/on-push.log , hier lohnt also ein Blick.

Noch ein kleiner Hinweis meinerseits, der eine oder andere von euch wird schnell feststellen, das GitLab selber im Admin Panel auch einen Menü Eintrag mit der Bezeichnung [Deploy Keys] bereitstellt, womit sich das oben gezeigte auch umsetzten lässt, aufgrund einiger bedenken bzgl. Sicherheit dieses allerdings nicht immer gewünscht ist, da alle Keys die dort hinterlegt werden automatisch, für allen Projekte (auch für zukünftige) direkt verwendet werden können.

Nützliche Links:

Enrico Labedzki

Autor: Enrico Labedzki

Enrico ist beruflich ganz schön rumgekommen – IT hat ihn aber immer beschäftigt. Nach einem Ausflug in die Selbstständigkeit im Bereich Webentwicklung und Network Solutions, wurde es dann Zeit Nägel mit Köpfen zu machen und endlich den Weg als Softwareentwickler und Systemintegrator einzuschlagen. In seiner Freizeit widmet sich der passionierte Bastler der Elektrotechnik und Animatronik. Bei Netways bereichert er mit seinem vielseitigen Know-How das Managed Service-Team.

Funktionales Programmieren in PHP

Letztens bin ich auf Github auf ein interessantes Projekt gestoßen, das ich euch nicht vorenthalten möchte. Ihor Burlachenko bietet mit PHP NSPL eine großartige Bibliothek, um alltägliche Programmieraufgaben elegant und funktional zu lösen. Sein Projekt auf Github ist ausführlich dokumentiert und bietet einige Beispiele. Also am besten gleich vorbeischauen oder hier den ersten Eindruck gewinnen:

// Get user ids
 
// NSPL
$userIds = map(propertyGetter('id'), $users);
 
// vs PHP
$userIds = array_map(
    function ($user) {
        return $user->id;
    },
    $users
);
 
// Filter active users
 
// NSPL
$activeUsers = filter(methodCaller('isActive'), $users);
 
// vs PHP
$activeUsers = array_filter(
    $users,
    function ($user) {
        return $user->isActive();
    }
);

Eric Lippmann

Autor: Eric Lippmann

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er sich für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.