{"id":11579,"date":"2016-07-23T01:56:11","date_gmt":"2016-07-22T23:56:11","guid":{"rendered":"https:\/\/tuxproject.de\/blog\/?p=11579"},"modified":"2019-05-23T11:30:28","modified_gmt":"2019-05-23T09:30:28","slug":"dynamische-single-serving-sites-mit-openbsd-bordmitteln-erstellen","status":"publish","type":"post","link":"https:\/\/tuxproject.de\/blog\/2016\/07\/dynamische-single-serving-sites-mit-openbsd-bordmitteln-erstellen\/","title":{"rendered":"Dynamische Single-Serving Sites mit OpenBSD-Bordmitteln erstellen"},"content":{"rendered":"<p>Unter \u201cSin\u00adgle-Serv\u00ading Sites\u201d ver\u00adste\u00adht man gemein\u00adhin Web\u00adsites wie <a href=\"https:\/\/isitchristmas.com\/\">IsItChristmas.com<\/a>, <a href=\"http:\/\/whatismyip.org\/\">WhatIsMyIP.org<\/a> und <a href=\"http:\/\/www.purple.com\">purple.com<\/a>, die als Gege\u00adnen\u00adtwurf zu infor\u00adma\u00adtion\u00ads\u00fcber\u00adlade\u00adnen Web\u00adpor\u00adtal\u00aden meist genau eine einzige Frage beant\u00adworten. Es gibt in der Regel keine Unter\u00adseit\u00aden, keine <em>echte<\/em> Inter\u00adak\u00adtiv\u00adit\u00e4t und auch son\u00adst sind solche Web\u00adsites sehr angenehm anzuse\u00adhen.<\/p>\n<p>Meist ist so eine Web\u00adsite schlicht in sta\u00adtis\u00adchem HTML geschrieben wor\u00adden, denn ser\u00adver\u00adseit\u00adige Scripts sind eben\u00adso \u00fcber\u00adfl\u00fcs\u00adsig wie jQuery oder andere Per\u00adver\u00adsio\u00adnen des Mod\u00ader\u00adnen. Was aber, wenn man dem Besuch\u00ader etwa abh\u00e4ngig von sein\u00ader Browser\u00adsprache oder per Zufall\u00adsal\u00adgo\u00adrith\u00admus eine andere Ansicht pr\u00e4sen\u00adtieren will? Nat\u00fcr\u00adlich k\u00f6n\u00adnte man auf Javascript zur\u00fcck\u00adgreifen; wer das aber nicht m\u00f6chte, zum Beispiel, weil er sich nicht darauf ver\u00adlassen m\u00f6chte, dass der Besuch\u00ader Javascript aktiviert hat und einen mit dem geschriebe\u00adnen Code kom\u00adpat\u00adi\u00adblen Brows\u00ader benutzt, dem bleibt let\u00adztlich nur die Nutzung ser\u00adver\u00adseit\u00adi\u00adgen Codes, etwa Perl oder PHP, \u00fcbrig.<\/p>\n<p>Nun gilt f\u00fcr Serv\u00ader mehr noch als f\u00fcr einen Lap\u00adtop oder PC, dass jede zus\u00e4t\u00adzlich instal\u00adlierte Soft\u00adware wegen m\u00f6glich\u00ader noch unent\u00addeck\u00adter oder unko\u00adr\u00adrigiert\u00ader Fehler ein poten\u00adzielles Sicher\u00adheit\u00adsrisiko birgt, wozu auch Skript\u00adsprachen, Web\u00adserver\u00adsoft\u00adware und sog\u00adar Texte\u00add\u00adi\u00adtoren z\u00e4hlen. Dazu kommt der Ressourcenhunger, den manch\u00ader <em>stack<\/em> mit sich bringt: Benutzt man etwa die beliebte Kom\u00adbi\u00adna\u00adtion aus dem Web\u00adserv\u00ader <tt>nginx<\/tt> und der Sprache PHP im FastC\u00adGI-Modus (<tt>php-fcgi<\/tt>), ist es keine Sel\u00adtenheit, dass auch im <em>Leer\u00adlauf<\/em> diverse Prozesse an den Sys\u00adtem\u00adres\u00adsourcen knab\u00adbern.<\/p>\n<p>L\u00e4sst sich eine solche Weban\u00adwen\u00addung auch pro\u00adgram\u00admieren, ohne den Serv\u00ader unn\u00f6tiger Last und Gefahren auszuset\u00adzen? <!--more-->Nun, je nach Betrieb\u00adssys\u00adtem stellt das tat\u00ads\u00e4ch\u00adlich kein Prob\u00adlem dar, denn zum Beispiel OpenB\u00adSD ver\u00adf\u00fcgt von Haus aus \u00fcber einen brauch\u00adbaren Texte\u00add\u00adi\u00adtor (<tt>vi<\/tt>), einen FastC\u00adGI unter\u00adst\u00fctzen\u00adden Web\u00adserv\u00ader (<tt>httpd<\/tt> mit <tt>slow\u00adc\u00adgi<\/tt>) sowie einen C\u2011Compiler (<tt>cc<\/tt>). C mag nicht ger\u00adade eine beson\u00adders ein\u00adsteiger\u00adfre\u00adundliche Sprache sein, aber f\u00fcr ein biss\u00adchen Tex\u00adtaus\u00adgabe ist nicht viel Code n\u00f6tig. Ich mach\u2019 mal ein Beispiel.<\/p>\n<p>Wir gehen im Fol\u00adgen\u00adden davon aus, dass wir eine beste\u00adhende Web\u00adsite namens \u201cSind-wir-schon-verloren.net\u201d haben, die bish\u00ader aus einem unter OpenB\u00adSD in einem <tt>nginx<\/tt>-Serv\u00ader laufend\u00aden PHP-Script beste\u00adht, das, wenn ein englis\u00adchsprachiger Besuch\u00ader es aufruft, \u201cYes\u201d, son\u00adst \u201cJa\u201d aus\u00adgibt.<\/p>\n<pre lang=\"html4strict\" escaped=\"true\">&lt;!doctype html&gt;\n&lt;html&gt;\n  &lt;head&gt;\n    &lt;title&gt;Sind wir schon verloren?&lt;\/title&gt;\n  &lt;\/head&gt;\n  &lt;body&gt;\n    &lt;h1&gt;&lt;?php\n      \/* Browsersprache ermitteln: *\/\n      if (substr($_SERVER[\"HTTP_ACCEPT_LANGUAGE\"],0,2) == \"en\") {\n          \/* Aha, ein englischsprachiger Nutzer! *\/\n          echo \"Yes\";\n      }\n      else {\n          \/* Aha, kein englischsprachiger Nutzer! *\/\n          echo \"Ja\";\n      }\n    ?&gt;&lt;\/h1&gt;\n  &lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>So weit, so ver\u00adst\u00e4ndlich. PHP hat den <em>Vorteil<\/em>, dass man es ein\u00adfach in beste\u00adhende HTML-Seit\u00aden inte\u00adgri\u00aderen kann und der Web\u00adserv\u00ader erledigt den Rest. Aber wir wollen ja Ressourcen sparen, also pro\u00adgram\u00admieren wir das ganze Ding mal neu.<\/p>\n<p>Wenn ihr Web\u00adserv\u00ader wie <tt>nginx<\/tt> oder <tt>lighttpd<\/tt> ein\u00adset\u00adzt, l\u00e4uft PHP bei euch ver\u00admut\u00adlich bere\u00adits im FastC\u00adGI-Modus. FastC\u00adGI bedeutet im Wesentlichen, dass der Web\u00adserv\u00ader laufend einige Kopi\u00aden von PHP im Spe\u00adich\u00ader h\u00e4lt, die bei ein\u00ader Anfrage schnell das <em>gew\u00fcn\u00adschte Ergeb\u00adnis<\/em> berech\u00adnen, hier also aus der Liste der Aufruf\u00advari\u00adablen die \u00fcber\u00admit\u00adtelte Liste der unter\u00adst\u00fctzten Sprachen her\u00adaus\u00adsuchen und die ersten zwei Zeichen dieser Liste, zum Beispiel \u201cde\u201d f\u00fcr Deutsch oder \u201cen\u201d f\u00fcr Englisch als Haupt\u00adsprache, mit der Zeichen\u00adkette <tt>\u201cen\u201d<\/tt> ver\u00adgle\u00adichen. Das klingt nicht nur wie der wohlbekan\u00adnte Schuss aus Kanonen auf Spatzen, es hat auch \u00e4hn\u00adliche Kon\u00adse\u00adquen\u00adzen. Dass PHP selb\u00adst im Leer\u00adlauf gele\u00adgentlich CPU-Zeit f\u00fcr sich beansprucht, ist nur eine davon.<\/p>\n<p>Warum also bei jedem Aufruf die ganze HTML-Seite neu kom\u00adpilieren? Viel effizien\u00adter ist es doch, ein bere\u00adits vorkom\u00adpiliertes Pro\u00adgramm hin\u00adter dieses FastC\u00adGI zu h\u00e4n\u00adgen. Damit diese wiederum zus\u00e4t\u00adzliche Soft\u00adware keinen Unsinn anstellt, k\u00f6n\u00adnen wir sie mit dem OpenB\u00adSD-Befehl <tt>pledge()<\/tt> <a href=\"http:\/\/www.heise.de\/ix\/meldung\/Eingeschraenkte-Privilegien-Unix-Derivat-OpenBSD-5-9-veroeffentlicht-3157290.html\">unter Kon\u00adtrolle bekom\u00admen<\/a>. Los geht\u2019s!<\/p>\n<p>Die C\u2011Version unser\u00ader PHP-Seite sieht so aus:<\/p>\n<pre lang=\"c\" escaped=\"true\">\/* Erst mal ein paar Funktionen nachladen: *\/\n#include &lt;stdlib.h&gt;  \/* Standardbibliothek *\/\n#include &lt;stdio.h&gt;   \/* puts() *\/\n#include &lt;string.h&gt;  \/* strncmp() *\/\n#include &lt;stdbool.h&gt; \/* bool *\/\n#ifdef __OpenBSD__\n\/* Unter OpenBSD k\u00f6nnen wir mit pledge() die Sicherheit erh\u00f6hen: *\/\n#include &lt;unistd.h&gt;  \/* pledge() *\/\n#include &lt;err.h&gt;     \/* err() *\/\n#endif\n\nint main(void) {\n#ifdef __OpenBSD__\n    if (-1 == pledge(\"stdio\", NULL)) {\n        \/* Wir wollen nicht zu viele Befugnisse! *\/\n        err(EXIT_FAILURE, \"pledge\");\n    }\n#endif\n\n    \/* In der Umgebungsvariablen HTTP_ACCEPT_LANGUAGE\n     * stehen die Browsersprachen des Besuchers (weiter-\n     * geleitet per FastCGI):\n     *\/\n    const char* curr_language = getenv(\"HTTP_ACCEPT_LANGUAGE\");\n    bool is_en = false;\n\n    if (curr_language != NULL) {\n        \/* is_en auf \"true\" setzen, falls die ersten zwei\n         * Zeichen \"en\" sind und die Sprache nicht NULL\n         * (also der Browser des Besuchers nicht schweigsam)\n         * ist:\n         *\/\n        is_en = strncmp(curr_language, \"en\", 2) == 0;\n    }\n\n    \/* HTTP-Statuscodes ausgeben: *\/\n    puts(\"Status: 200 OK\\r\");\n    puts(\"Content-Type: text\/html\\r\");\n    puts(\"\\r\");\n\n    \/* Es folgt die eigentliche Website. *\/\n    puts(\" !doctype html&gt;\");\n    puts(\"<html><head>\");\n    puts(\"    <title>Sind wir schon verloren?<\/title>\");\n    puts(\"  <\/head>\");\n    puts(\"  <body>\");\n    puts(\"    <h1>\");\n\n    \/* Falls is_en \"true\" ist, gib \"Yes\" aus, sonst \"Ja\": *\/\n    if (is_en) {\n        puts(\"Yes\");\n    }\n    else {\n        puts(\"Ja\");\n    }\n\n    \/* Der Rest der Seite: *\/\n    puts(\"    <\/h1>\");\n    puts(\"  <\/body>\");\n    puts(\"\");\n\n    return(EXIT_SUCCESS);\n}<\/html><\/pre>\n<p>Dieses Script kann jet\u00adzt zum Beispiel als <tt>website.c<\/tt> gespe\u00adichert und anschlie\u00dfend mit OpenB\u00adS\u00adDs C\u2011Compiler zu einem Pro\u00adgramm gemacht wer\u00adden; ich empfehle die Aktivierung von War\u00adnun\u00adgen und Opti\u00admierun\u00adgen sowie sta\u00adtis\u00adch\u00ader Kom\u00adpilierung, damit keine exter\u00adnen Abh\u00e4ngigkeit\u00aden bei pl\u00f6t\u00adzlichem Aus\u00adfall die Funk\u00adtions\u00adf\u00e4higkeit ein\u00adschr\u00e4nken k\u00f6n\u00adnen:<\/p>\n<pre>cc -static -Wall -O3 -o website website.c<\/pre>\n<p>Ein ein\u00adfach\u00ader Aufruf des Pro\u00adgramms <tt>web\u00adsite<\/tt> gibt jet\u00adzt, wenn alles geklappt hat, den Sta\u00adtushead\u00ader sowie den deutschsprachi\u00adgen (da <tt>HTTP_ACCEPT_LANGUAGE<\/tt> lokal <em>nat\u00fcr\u00adlich<\/em> nicht existiert) HTML-Code aus. Als n\u00e4ch\u00adstes m\u00fcssen wir nur noch den Web\u00adserv\u00ader kon\u00adfig\u00aduri\u00aderen. Falls ihr bish\u00ader <tt>nginx<\/tt> benutzt habt, ist es sin\u00adnvoll, ihn zu deak\u00adtivieren, bevor der <tt>httpd<\/tt> ges\u00adtartet wird. Au\u00dfer\u00addem sollte <tt>slow\u00adc\u00adgi<\/tt>, die <a href=\"http:\/\/man.openbsd.org\/slowcgi.8\">auf das Wesentliche reduzierte<\/a> FastC\u00adGI-Imple\u00admen\u00adtierung von OpenB\u00adSD, aktiviert wer\u00adden:<\/p>\n<pre># rcctl stop nginx\n# rcctl disable nginx\n# rcctl enable slowcgi\n# rcctl start slowcgi<\/pre>\n<p>Jet\u00adzt fol\u00adgt der let\u00adzte Schritt: Beim Aufruf von <em>Sind-wir-schon-verloren.net<\/em> soll der <tt>httpd<\/tt> die Aus\u00adgabe des oben geschriebe\u00adnen FastC\u00adGI-Pro\u00adgramms zur\u00fcck\u00adgeben. Es ist nicht damit getan, den Serv\u00ader <em>auf das Verze\u00adich\u00adnis zeigen<\/em> zu lassen, denn mit Bin\u00e4r\u00addat\u00aden kann er hier nichts anfan\u00adgen. Ein Ein\u00adtrag in eur\u00ader <tt>\/etc\/httpd.conf<\/tt> schafft Abhil\u00adfe:<\/p>\n<pre>ext_addr=\"egress\"\n\nserver \"sind-wir-schon-verloren.net\" {\n    # Der Server soll sich mit und ohne \"www\" gleich verhalten\n    alias \"www.sind-wir-schon-verloren.net\"\n    listen on $ext_addr port 80\n\n    # Der Aufruf \"zeigt\" direkt auf das FastCGI-Script:\n    root \"\/pfad\/zu\/eurem\/programm\"\n\n    # FastCGI aktivieren:\n    fastcgi socket \"\/run\/slowcgi.sock\"\n}<\/pre>\n<p>Ein beherztes \u2014 falls noch nicht geschehen \u2014 Aktivieren von <tt>httpd<\/tt> und einen Neustart (<tt>rcctl restart httpd<\/tt>) des Dae\u00admons sp\u00e4ter ist eure Weban\u00adwen\u00addung wieder von au\u00dfen erre\u00adich\u00adbar, als w\u00e4re sie nie weg gewe\u00adsen \u2014 sie ben\u00f6tigt nur keine zus\u00e4t\u00adzlichen Pakete mehr. Da nun alles wie gew\u00fcn\u00adscht funk\u00adtion\u00adiert, k\u00f6n\u00adnen wir schlie\u00dflich unseren jet\u00adzt nur noch unn\u00f6tig Platz ver\u00adschwen\u00adden\u00adden <tt>nginx<\/tt>-Serv\u00ader und die PHP-Instal\u00adla\u00adtion ent\u00adfer\u00adnen. Dabei sollte darauf geachtet wer\u00adden, dass keine <em>alten<\/em> PHP-Mod\u00adule \u00fcbrig bleiben, weshalb eine vorherige Auflis\u00adtung der instal\u00adlierten PHP-Teile rat\u00adsam ist:<\/p>\n<pre># pkg_info | grep php\n# pkg_delete php-fpm nginx<\/pre>\n<p>M\u00f6glicher\u00adweise bit\u00adtet <tt>pkg_delete<\/tt> euch nun darum, \u00fcbrig gebliebene Kon\u00adfig\u00adu\u00adra\u00adtionsverze\u00adich\u00adnisse zu l\u00f6schen. Das k\u00f6n\u00adnt ihr gefahr\u00adlos tun. Schaut auch in eur\u00ader <tt>\/etc\/rc.conf.local<\/tt> nach, ob <tt>nginx<\/tt> und <tt>php-fpm<\/tt> noch in euren Startscripts auf\u00adtauchen.<\/p>\n<p>Fer\u00adtig!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Unter \u201cSin\u00ad\u00adgle-Serv\u00ading Sites\u201d ver\u00adste\u00adht man gemein\u00adhin Web\u00adsites wie IsItChristmas.com, WhatIsMyIP.org und purple.com, die als Gege\u00adnen\u00adtwurf zu infor\u00adma\u00adtion\u00ads\u00fcber\u00adlade\u00adnen Web\u00adpor\u00adtal\u00aden meist genau eine einzige Frage beant\u00adworten. Es gibt in der Regel keine Unter\u00adseit\u00aden, keine echte Inter\u00adak\u00adtiv\u00adit\u00e4t und auch son\u00adst sind solche Web\u00adsites sehr angenehm anzuse\u00adhen. Meist ist so eine Web\u00adsite schlicht in sta\u00adtis\u00adchem HTML geschrieben wor\u00adden, denn \u2026<\/p>\n<p><a href=\"https:\/\/tuxproject.de\/blog\/2016\/07\/dynamische-single-serving-sites-mit-openbsd-bordmitteln-erstellen\/\" class=\"more-link\">\u2018Dynamis\u00adche Sin\u00adgle-Serv\u00ading Sites mit OpenB\u00adSD-Bor\u00add\u00admit\u00adteln erstellen\u2019 weit\u00ader\u00adlesen \u00bb<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wp_typography_post_enhancements_disabled":false,"footnotes":""},"categories":[19],"tags":[],"series":[],"class_list":["post-11579","post","type-post","status-publish","format-standard","hentry","category-nerdkrams"],"share_on_mastodon":{"url":"","error":""},"wp-worthy-pixel":{"ignored":false,"public":null,"server":null,"url":null},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/posts\/11579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/comments?post=11579"}],"version-history":[{"count":0,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/posts\/11579\/revisions"}],"wp:attachment":[{"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/media?parent=11579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/categories?post=11579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/tags?post=11579"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/tuxproject.de\/blog\/wp-json\/wp\/v2\/series?post=11579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}