Nerdkrams
Dyna­mi­sche Sin­gle-Ser­ving Sites mit Open­BSD-Bord­mit­teln erstel­len

Unter „Sin­gle-Ser­ving Sites“ ver­steht man gemein­hin Web­sites wie IsItChristmas.com, WhatIsMyIP.org und purple.com, die als Gegen­ent­wurf zu infor­ma­ti­ons­über­la­de­nen Web­por­ta­len meist genau eine ein­zi­ge Fra­ge beant­wor­ten. Es gibt in der Regel kei­ne Unter­sei­ten, kei­ne ech­te Inter­ak­ti­vi­tät und auch sonst sind sol­che Web­sites sehr ange­nehm anzu­se­hen.

Meist ist so eine Web­site schlicht in sta­ti­schem HTML geschrie­ben wor­den, denn ser­ver­sei­ti­ge Scripts sind eben­so über­flüs­sig wie jQuery oder ande­re Per­ver­sio­nen des Moder­nen. Was aber, wenn man dem Besu­cher etwa abhän­gig von sei­ner Brow­ser­spra­che oder per Zufalls­al­go­rith­mus eine ande­re Ansicht prä­sen­tie­ren will? Natür­lich könn­te man auf Java­script zurück­grei­fen; wer das aber nicht möch­te, zum Bei­spiel, weil er sich nicht dar­auf ver­las­sen möch­te, dass der Besu­cher Java­script akti­viert hat und einen mit dem geschrie­be­nen Code kom­pa­ti­blen Brow­ser benutzt, dem bleibt letzt­lich nur die Nut­zung ser­ver­sei­ti­gen Codes, etwa Perl oder PHP, übrig.

Nun gilt für Ser­ver mehr noch als für einen Lap­top oder PC, dass jede zusätz­lich instal­lier­te Soft­ware wegen mög­li­cher noch unent­deck­ter oder unkor­ri­gier­ter Feh­ler ein poten­zi­el­les Sicher­heits­ri­si­ko birgt, wozu auch Skript­spra­chen, Web­ser­ver­soft­ware und sogar Text­edi­to­ren zäh­len. Dazu kommt der Res­sour­cen­hun­ger, den man­cher stack mit sich bringt: Benutzt man etwa die belieb­te Kom­bi­na­ti­on aus dem Web­ser­ver nginx und der Spra­che PHP im Fast­C­GI-Modus (php-fcgi), ist es kei­ne Sel­ten­heit, dass auch im Leer­lauf diver­se Pro­zes­se an den System­res­sour­cen knab­bern.

Lässt sich eine sol­che Web­an­wen­dung auch pro­gram­mie­ren, ohne den Ser­ver unnö­ti­ger Last und Gefah­ren aus­zu­set­zen? Nun, je nach Betriebs­sy­stem stellt das tat­säch­lich kein Pro­blem dar, denn zum Bei­spiel Open­BSD ver­fügt von Haus aus über einen brauch­ba­ren Text­edi­tor (vi), einen Fast­C­GI unter­stüt­zen­den Web­ser­ver (httpd mit slow­c­gi) sowie einen C‑Compiler (cc). C mag nicht gera­de eine beson­ders ein­steig­er­freund­li­che Spra­che sein, aber für ein biss­chen Text­aus­ga­be ist nicht viel Code nötig. Ich mach‘ mal ein Bei­spiel.

Wir gehen im Fol­gen­den davon aus, dass wir eine bestehen­de Web­site namens „Sind-wir-schon-verloren.net“ haben, die bis­her aus einem unter Open­BSD in einem nginx-Ser­ver lau­fen­den PHP-Script besteht, das, wenn ein eng­lisch­spra­chi­ger Besu­cher es auf­ruft, „Yes“, sonst „Ja“ aus­gibt.

<!doctype html>
<html>
  <head>
    <title>Sind wir schon verloren?</title>
  </head>
  <body>
    <h1><?php
      /* Browsersprache ermitteln: */
      if (substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) == "en") {
          /* Aha, ein englischsprachiger Nutzer! */
          echo "Yes";
      }
      else {
          /* Aha, kein englischsprachiger Nutzer! */
          echo "Ja";
      }
    ?></h1>
  </body>
</html>

So weit, so ver­ständ­lich. PHP hat den Vor­teil, dass man es ein­fach in bestehen­de HTML-Sei­ten inte­grie­ren kann und der Web­ser­ver erle­digt den Rest. Aber wir wol­len ja Res­sour­cen spa­ren, also pro­gram­mie­ren wir das gan­ze Ding mal neu.

Wenn ihr Web­ser­ver wie nginx oder lighttpd ein­setzt, läuft PHP bei euch ver­mut­lich bereits im Fast­C­GI-Modus. Fast­C­GI bedeu­tet im Wesent­li­chen, dass der Web­ser­ver lau­fend eini­ge Kopien von PHP im Spei­cher hält, die bei einer Anfra­ge schnell das gewünsch­te Ergeb­nis berech­nen, hier also aus der Liste der Auf­ruf­va­ria­blen die über­mit­tel­te Liste der unter­stütz­ten Spra­chen her­aus­su­chen und die ersten zwei Zei­chen die­ser Liste, zum Bei­spiel „de“ für Deutsch oder „en“ für Eng­lisch als Haupt­spra­che, mit der Zei­chen­ket­te „en“ ver­glei­chen. Das klingt nicht nur wie der wohl­be­kann­te Schuss aus Kano­nen auf Spat­zen, es hat auch ähn­li­che Kon­se­quen­zen. Dass PHP selbst im Leer­lauf gele­gent­lich CPU-Zeit für sich bean­sprucht, ist nur eine davon.

War­um also bei jedem Auf­ruf die gan­ze HTML-Sei­te neu kom­pi­lie­ren? Viel effi­zi­en­ter ist es doch, ein bereits vor­kom­pi­lier­tes Pro­gramm hin­ter die­ses Fast­C­GI zu hän­gen. Damit die­se wie­der­um zusätz­li­che Soft­ware kei­nen Unsinn anstellt, kön­nen wir sie mit dem Open­BSD-Befehl pledge() unter Kon­trol­le bekom­men. Los geht’s!

Die C‑Version unse­rer PHP-Sei­te sieht so aus:

/* Erst mal ein paar Funktionen nachladen: */
#include <stdlib.h>  /* Standardbibliothek */
#include <stdio.h>   /* puts() */
#include <string.h>  /* strncmp() */
#include <stdbool.h> /* bool */
#ifdef __OpenBSD__
/* Unter OpenBSD können wir mit pledge() die Sicherheit erhöhen: */
#include <unistd.h>  /* pledge() */
#include <err.h>     /* err() */
#endif
 
int main(void) {
#ifdef __OpenBSD__
    if (-1 == pledge("stdio", NULL)) {
        /* Wir wollen nicht zu viele Befugnisse! */
        err(EXIT_FAILURE, "pledge");
    }
#endif
 
    /* In der Umgebungsvariablen HTTP_ACCEPT_LANGUAGE
     * stehen die Browsersprachen des Besuchers (weiter-
     * geleitet per FastCGI):
     */
    const char* curr_language = getenv("HTTP_ACCEPT_LANGUAGE");
    bool is_en = false;
 
    if (curr_language != NULL) {
        /* is_en auf "true" setzen, falls die ersten zwei
         * Zeichen "en" sind und die Sprache nicht NULL
         * (also der Browser des Besuchers nicht schweigsam)
         * ist:
         */
        is_en = strncmp(curr_language, "en", 2) == 0;
    }
 
    /* HTTP-Statuscodes ausgeben: */
    puts("Status: 200 OK\r");
    puts("Content-Type: text/html\r");
    puts("\r");
 
    /* Es folgt die eigentliche Website. */
    puts("< !doctype html>");
    puts("<html>");
    puts("  <head>");
    puts("    <title>Sind wir schon verloren?</title>");
    puts("  </head>");
    puts("  <body>");
    puts("    <h1>");
 
    /* Falls is_en "true" ist, gib "Yes" aus, sonst "Ja": */
    if (is_en) {
        puts("Yes");
    }
    else {
        puts("Ja");
    }
 
    /* Der Rest der Seite: */
    puts("    </h1>");
    puts("  </body>");
    puts("</html>");
 
    return(EXIT_SUCCESS);
}

Die­ses Script kann jetzt zum Bei­spiel als website.c gespei­chert und anschlie­ßend mit Open­BS­Ds C‑Compiler zu einem Pro­gramm gemacht wer­den; ich emp­feh­le die Akti­vie­rung von War­nun­gen und Opti­mie­run­gen sowie sta­ti­scher Kom­pi­lie­rung, damit kei­ne exter­nen Abhän­gig­kei­ten bei plötz­li­chem Aus­fall die Funk­ti­ons­fä­hig­keit ein­schrän­ken kön­nen:

cc -static -Wall -O3 -o website website.c

Ein ein­fa­cher Auf­ruf des Pro­gramms web­site gibt jetzt, wenn alles geklappt hat, den Sta­tus­hea­der sowie den deutsch­spra­chi­gen (da HTTP_ACCEPT_LANGUAGE lokal natür­lich nicht exi­stiert) HTML-Code aus. Als näch­stes müs­sen wir nur noch den Web­ser­ver kon­fi­gu­rie­ren. Falls ihr bis­her nginx benutzt habt, ist es sinn­voll, ihn zu deak­ti­vie­ren, bevor der httpd gestar­tet wird. Außer­dem soll­te slow­c­gi, die auf das Wesent­li­che redu­zier­te Fast­C­GI-Imple­men­tie­rung von Open­BSD, akti­viert wer­den:

# rcctl stop nginx
# rcctl disable nginx
# rcctl enable slowcgi
# rcctl start slowcgi

Jetzt folgt der letz­te Schritt: Beim Auf­ruf von Sind-wir-schon-verloren.net soll der httpd die Aus­ga­be des oben geschrie­be­nen Fast­C­GI-Pro­gramms zurück­ge­ben. Es ist nicht damit getan, den Ser­ver auf das Ver­zeich­nis zei­gen zu las­sen, denn mit Binär­da­ten kann er hier nichts anfan­gen. Ein Ein­trag in eurer /etc/httpd.conf schafft Abhil­fe:

ext_addr="egress"

server "sind-wir-schon-verloren.net" {
    # Der Server soll sich mit und ohne "www" gleich verhalten
    alias "www.sind-wir-schon-verloren.net"
    listen on $ext_addr port 80

    # Der Aufruf "zeigt" direkt auf das FastCGI-Script:
    root "/pfad/zu/eurem/programm"

    # FastCGI aktivieren:
    fastcgi socket "/run/slowcgi.sock"
}

Ein beherz­tes – falls noch nicht gesche­hen – Akti­vie­ren von httpd und einen Neu­start (rcctl restart httpd) des Dae­mons spä­ter ist eure Web­an­wen­dung wie­der von außen erreich­bar, als wäre sie nie weg gewe­sen – sie benö­tigt nur kei­ne zusätz­li­chen Pake­te mehr. Da nun alles wie gewünscht funk­tio­niert, kön­nen wir schließ­lich unse­ren jetzt nur noch unnö­tig Platz ver­schwen­den­den nginx-Ser­ver und die PHP-Instal­la­ti­on ent­fer­nen. Dabei soll­te dar­auf geach­tet wer­den, dass kei­ne alten PHP-Modu­le übrig blei­ben, wes­halb eine vor­he­ri­ge Auf­li­stung der instal­lier­ten PHP-Tei­le rat­sam ist:

# pkg_info | grep php
# pkg_delete php-fpm nginx

Mög­li­cher­wei­se bit­tet pkg_delete euch nun dar­um, übrig geblie­be­ne Kon­fi­gu­ra­ti­ons­ver­zeich­nis­se zu löschen. Das könnt ihr gefahr­los tun. Schaut auch in eurer /etc/rc.conf.local nach, ob nginx und php-fpm noch in euren Start­scripts auf­tau­chen.

Fer­tig!

Senfecke:

  1. Com­pi­liert mit musl-gcc ist das sta­tisch gelink­te Pro­gramm auch schön klein:
    ‑rwx—— 1 har­dy har­dy 5704 Jul 23 09:01 web­site*

    • Mei­nes Wis­sens funk­tio­niert musl nicht unter Open­BSD, zumin­dest konn­te ich bis­her kei­nen Hin­weis dar­auf fin­den. Star­tet das Pro­gramm denn über­haupt?

      • Star­tet das Pro­gramm denn über­haupt?

        Natür­lich nicht mit Dei­nem Mulu-Mulu-BSD. Mit Linux selbst­ver­ständ­lich ja.

            • Tscha, taugt halt nix.

              Unter Linux müss­te man sich irgend­ei­nes zusam­men­ge­klecker­ten Dritt­an­bie­ter­ser­vers (nginx oder so) bedie­nen und den pledge()-Auf­ruf aus dem Code ent­fer­nen, dann soll­te es aller­dings auch gehen.

              Nach­trag: Ich habe den Code oben mal geän­dert, jetzt soll­te er auch unter ande­ren Syste­men pro­blem­los funk­tio­nie­ren.

Comments are closed.

https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_smilenew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_biggrin2.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_sadnew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_eek.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_shocked.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_confusednew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_coolnew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_lol.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_madnew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_aufsmaul.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_seb_zunge.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_blushnew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_frown.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_twistedevil1.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_twistedevil2.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/icon_mad.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_rolleyesnew.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_wink2.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_idea2.gif  https://tuxproject.de/blog/wp-content/plugins/wp-monalisa/icons/smiley_emoticons_arrow2.gif 
mehr …
 

Erlaubte Tags:
<strong> <em> <pre> <code> <a href="" title=""> <img src="" title="" alt=""> <blockquote> <q> <b> <i> <del> <tt> <span style=""> <strike>

Datenschutzhinweis: Deine IP-Adresse wird nicht gespeichert. Details findest du hier.