Linux Server setup

Reverse Proxy Server einrichten

Nachdem wir im letzten Kapitel (E-Mail Weiterleitung) einen Mailserver installiert haben, werden wir jetzt einen Webserver einrichten.

In diesem Kapitel werden wir einen Serverdienst soweit einrichten, dass über unsere Domain eine statische HTML Seite abrufbar ist. Diese Vorarbeit ist auch Voraussetzung, um im nächsten Kapitel TLS Zertifikate durch anerkannte Zertifizierungsstellen zu bekommen.

Da wir damit rechnen müssen, dass Anfragen nicht nur idealerweise auf eine verschlüsselte Adresse (z. B. https://www.linuxserversetup.com) abzielen, müssen wir alle anderen Möglichkeiten zusätzlich berücksichtigen. Unsere Domain, aber auch die IP Adresse des Servers, kann sehr verschieden über das HTTP Protokoll angefragt werden.

Legitime Beispiele:

Über Port 80 (HTTP):

  • linuxserversetup.com
  • www.linuxserversetup.com
  • http://linuxserversetup.com
  • http://www.linuxserversetup.com

Über Port 443 (HTTPS):

  • https://linuxserversetup.com
  • https://www.linuxserversetup.com (bevorzugte Adresse!)

Über die IP Adresse:

  • 116.203.69.89 (Port 80)
  • http://116.203.69.89 (Port 80)
  • https://116.203.69.89 (Port 443)

Als Beispiel für diese Anleitung lege ich https://www.linuxserversetup.com als bevorzugte Adresse fest. Nicht-www Anfragen sollen automatisch auf die www-Adresse umleiten. Alle anderen Varianten sollen entweder dorthin umleiten oder eine leere Seite zurückgeben. Letzteres gilt vor allem, wenn jemand zum Beispiel die IP des Servers über einen Browser aufruft.

Wir brauchen also eine Server Software, die auf HTTP (80) und HTTPS (443) Ports reagiert. Unsere Firewall muss natürlich auch diese "öffentlichen" Ports durchlassen. Für ein einfaches "Hallo Welt" im Browser bräuchten wir im Grunde nur einen Serverdienst, wie zum Beispiel NGINX oder Apache, der eine statische HTML Datei an den Klienten zurück sendet. Aber wenn wir schon einen eigenen Server betreiben, dann bauen wir auch eine Infrastruktur auf, die skalierbar ist und produktiv eingesetzt werden kann. Es muss früher oder später möglich sein, weitere Domains zu hosten und diese mit verschiedenen serverseitigen Technologien (Backends) betreiben zu können.

Die Problematik ist, dass verschiedene Serverdienste (NGINX, Apache, Node.js, etc.) zwar gleichzeitig laufen, aber nicht dieselben Ports teilen können. Zum Beispiel kann nicht eine Domain mit Apache und eine andere Domain mit Node.js gleichzeitig über den Port 80 betrieben werden. Wenn das versucht wird, würde folgendes passieren: Angenommen Apache startet zuerst und reserviert den Port 80, Node.js startet danach und versucht ebenfalls Port 80 zu binden. Das würde mit einer Fehlermeldung ("Port vergeben") fehlschlagen.

Die Lösung ist einfach. In den Vordergrund wird ein Serverdienst (Reverse Proxy) gestellt, der wie ein Verteiler funktioniert. Die "Außenwelt" kommuniziert nur mit diesem Reverse Proxy. Der kann externe Anfragen nach IP, Port, Domain, Subdomain oder auch URL filtern und sie an festgelegte interne Ports weiterleiten. Das entsprechende Backend wird dann darauf reagieren. Das funktioniert, weil Backends bzw. interne Serverdienste an unterschiedliche interne Ports gebunden sind, wodurch sie parallel laufen können.

Der Reverse Proxy wird auch für die Verschlüsselung der Datenübertragung nach Außen verantwortlich sein. Damit haben wir eine zentrale Stelle, die für die Herausgabe der TLS Zertifikate zuständig ist.

Weitere Vorteile sind: Lastenverteilung (Load Balancing) und Zwischenspeichern (Caching).

Ich werde mir die beiden Adressen linuxserversetup.com und dev.linuxserversetup.com einrichten. Weitere Domains oder Subdomains würden dem Schema einfach folgen.

Bevor es los geht erledigen wir an dieser Stelle eine sinnvolle Sache, damit wir später keine Probleme mit Schreibrechten bei den Web-Verzeichnissen bekommen. Es ist üblich Web-Projekte unter /var/www anzulegen. Diesen Ordner werden wir mit sudo anlegen müssen. Da wir aber später hauptsächlich mit tom arbeiten, legen wir ihn als neuen Eigentümer für diesen Ordner fest. So ist er berechtigt, dort Dateien und Ordner zu bearbeiten.

/var/www erstellen mit mkdir:


__$ sudo mkdir /var/www
 

Den Eigentümer ändern wir wie schon zuvor mit dem Befehl chown:


__$ sudo chown -R tom:tom /var/www
 

Die nächsten Schritte:


NGINX installieren

Als Reverse Proxy setzen wir NGINX ein. NGINX ist vielfältig, sehr robust und leicht konfigurierbar. Mittlerweile gehört die Software sogar Weltweit mit zu den meist eingesetzten Webserverdiensten.

Wir installieren NGINX wie gewohnt mit apt:


__$ sudo apt install -y nginx
 

Nach der Installation können wir die aktuelle Version abrufen:


__$ nginx -v
 

Außerdem hat nginx unter /var/www einen Ordner html mit einer HTML Datei darin erzeugt:


__$ ls -l /var/www
__$ ls -l /var/www/html
__$ less /var/www/html/index.nginx-debian.html
 

/var/www/html/index.nginx-debian.html


<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
	width: 35em;
	margin: 0 auto;
	font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p>
<p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/>Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Den Ordner samt Inhalt brauchen wir nicht bzw. werden ihn später mit etwas anderem ersetzen. Also können wir /var/www/html samt Inhalt löschen. Rekursiv löschen mit rm -r:


__$ sudo rm -r /var/www/html
 

Die globalen Einstellungen zu NGINX sind in der Datei /etc/nginx/nginx.conf:


__$ sudo nano /etc/nginx/nginx.conf
 

Hier können wir server_tokens off einkommentieren. Damit verhindern wir, dass die Server Version mit in den Antwort Header geschrieben wird. Sonst könnte zum Beispiel über die Webentwickler Konsole eines Browsers die Zeile Server: nginx/1.18.0 (Ubuntu) ausgelesen werden. Diese Information wird aus Sicherheitsgründen verschwiegen.

In dieser Datei können wir auch gzip und alle weiteren Einstellungen, die damit zusammenhängen (mit gzip_ anfangen) aktivieren. gzip komprimiert Daten für die Übertragung, spart also Bandbreite und erhöht die Übertragungsgeschwindigkeit.

Die vollständige /etc/nginx/nginx.conf sollte so aussehen:

/etc/nginx/nginx.conf


user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
  worker_connections 768;
  # multi_accept on;
}

http {

  ##
  # Basic Settings
  ##

  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  keepalive_timeout 65;
  types_hash_max_size 2048;
  server_tokens off;

  # server_names_hash_bucket_size 64;
  # server_name_in_redirect off;

  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  ##
  # SSL Settings
  ##

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
  ssl_prefer_server_ciphers on;

  ##
  # Logging Settings
  ##

  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  ##
  # Gzip Settings
  ##

  gzip on;

  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  ##
  # Virtual Host Configs
  ##

  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}

#mail {
  #  # See sample authentication script at:
  #  # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
  #
  #  # auth_http localhost/auth.php;
  #  # pop3_capabilities "TOP" "USER";
  #  # imap_capabilities "IMAP4rev1" "UIDPLUS";
  #
  #  server {
  #     listen     localhost:110;
  #     protocol   pop3;
  #     proxy      on;
  #  }
  #
  #  server {
  #     listen     localhost:143;
  #     protocol   imap;
  #     proxy      on;
  #  }
  #}

Dort ist auch definiert, wo die Log-Dateien für NGINX abgelegt werden. Da dürften aktuell leer sein:


__$ sudo less /var/log/nginx/access.log
__$ sudo less /var/log/nginx/error.log
 

Damit die Änderungen übernommen werden, starten wir NGINX neu:


__$ sudo systemctl restart nginx
 

Zum Überprüfen der aktuellen Konfiguration geben wir den Parameter -t an:


__$ sudo nginx -t
 

Wenn alles in Ordnung ist, sieht die Ausgabe so aus:


nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Schauen wir auch kurz nach dem aktuellen Status:


__$ sudo systemctl status nginx
 

Wenn es keine Probleme gibt, sollte der NGINX Status active (running) sein.

Mit folgendem Kommando startet NGINX mit jedem Server Neustart automatisch mit:


__$ sudo systemctl enable nginx
 

Bleibt noch die Freigabe durch die Firewall:


__$ sudo ufw allow 'Nginx Full'
 

Damit hätten wir die Grundkonfiguration für unseren Reverse Proxy erledigt.

Als nächstes legen wir einzelne Server Blöcke an, mit denen wir externe Anfragen separieren können.


Server Blocks

NGINX arbeitet mit Konfigurationseinheiten, sogenannte Server Blöcke. Auch wenn es möglich wäre die gesamte NGINX Konfiguration in eine Datei zu schreiben, ist es sinnvoller und übersichtlicher einzelne Zweckgebundene Dateien zu haben.

Wie bei anderen Serverdiensten auch, wird ein Zusammenspiel der Ordner sites-available und sites-enabled empfohlen.

Konfigurationsdateien werden in den Ordner /etc/nginx/sites-available abgelegt. Tatsächlich werden von denen nur die berücksichtigt, die über /etc/nginx/sites-enabled, mithilfe von symbolischen Verknüpfungen (Symlinks: ln), durch NGINX eingebunden werden. Das mag am Anfang mit nur einer Domain etwas übertrieben wirken, ist jedoch eine wichtige Grundordnung, wenn weitere Adressen dazu kommen.

Die Verknüpfungen in sites-enabled sorgen für eine gute Übersicht, auch wenn es mal in sites-available etwas chaotisch zugehen sollte.


Standard Server Block (default)

Mit dem ersten Server Block werden wir alle HTTP Anfragen abfangen und behandeln, die nicht zugeordnet werden können. Zum Beispiel, wenn die Server IP http://116.203.69.89 über den Browser aufgerufen wird. Für diesen Fall möchte ich grundsätzlich eine leere Seite anzeigen lassen.

NGINX hat bereits einen "default" angelegt. Die symbolische Verknüpfung liegt in /etc/nginx/sites-enabled:


__$ ls -l /etc/nginx/sites-enabled
 

Ausgabe:


lrwxrwxrwx 1 root root 34 Jan  3 19:15 default -> /etc/nginx/sites-available/default

Die tatsächliche Konfigurationsdatei (default) liegt entsprechend in /etc/nginx/sites-available:


__$ ls -l /etc/nginx/sites-available
 

Ausgabe:


-rw-r--r-- 1 root root 2416 Jan  3  2022 default

Bevor wir die default Datei bearbeiten, legen wir davon eine Sicherungskopie an:


__$ sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default-backup
 

Jetzt können wir die default Datei öffnen:


__$ sudo nano /etc/nginx/sites-available/default
 

Und den gesamten Inhalt mit Folgendem ersetzen (TIPP: STRG+k zum Löschen ganzer Zeilen):

/etc/nginx/sites-available/default


server {
  listen      80;
  listen      [::]:80;
  server_name _;

  root        /var/www/default/;
  index       index.htm;

  location / {
    try_files $uri $uri/ /index.htm;
  }

  location = /favicon.ico { access_log off; log_not_found off; }
}
 

Zur Erklärung:

  • server startet den Server Block.
  • listen reagiert auf die IPv4 und IPv6 Adresse mit jeweils Port 80.
  • server_name hat einen Platzhalter (_). Hier könnte auch ein Domainname eingetragen sein.
  • root zeigt auf einen Projekt Pfad.
  • index benennt eine Standard-Einstiegsdatei.
  • location / nimmt alle URL Pfade entgegen. Das kann http://116.203.69.89, aber auch http://116.203.69.89/a/b/c sein. Wenn der Überhang $uri durch try_files scheitert, wird /index.htm zurückgegeben (was bei dieser Default-Seite immer der Fall sein wird!).
  • location = /favicon.ico berücksichtigt, dass Browser generell ein Favicon anfragen. Da hier keines vorgesehen ist und wir die 403 und 404 Fehler nicht protokollieren wollen, schalten wir das Logging mit access_log und log_not_found für den Pfad /favicon.ico ab.

Den root Pfad müssen wir noch anlegen:


__$ mkdir /var/www/default
 

Und auch die Datei für index:


__$ nano /var/www/default/index.htm
 

Mir reicht hier eine leere Seite. Der Inhalt der index.htm könnte also ein valides HTML Gerüst sein:

/var/www/default/index.htm


<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>default index</title>
  </head>
  <body></body>
</html>
 

Nach dem speichern überprüfen wir die neue Konfiguration und starten NGINX neu:


__$ sudo nginx -t
__$ sudo systemctl restart nginx
 

Wenn es keine Probleme gibt, können wir jetzt die leere Seite über http://116.203.69.89 im Browser aufrufen.

Da als server_name ein Platzhalter eingetragen ist, wird der Domainaufruf (http://linuxserversetup.com) ebenfalls von diesem Server Block abgefangen. Das ändert sich mit dem nächsten Abschnitt.


Server Block für eine Domain (www erzwingen)

Mit diesem Server Block konfigurieren wir die Adressen http://linuxserversetup.com und http://www.linuxserversetup.com. Wobei automatisch auf www umgeleitet werden soll. Wir legen dazu eine neue Konfigurationsdatei unter /etc/nginx/sites-available an, für die wir gleich auch eine symbolische Verknüpfung in /etc/nginx/sites-enabled hinterlegen.

Es macht Sinn die Datei ähnlich der Adresse zu benennen, nur das sie mit der Top-Level-Domain beginnt, gefolgt von der Domain und eventuell Subdomain. Als Konfigurationsdatei sollte sie außerdem mit .conf enden. In einer alphabetisch geordneten Liste bleiben dadurch gleiche Domainnamen untereinander.


__$ sudo nano /etc/nginx/sites-available/com.linuxserversetup.conf
 

Der Inhalt ist ähnlich wie zuvor, nur das wir jetzt bei server_name zwei Adressen und als root Ordner /var/www/com.linuxserversetup angeben. Wir vergeben auch den Parameter default_server, was NGINX mitteilt, diesen Server Block zu priorisieren. Der erste Server Block erzwingt die Umleitung nach www. Es ist zu empfehlen $server_name anstelle von $host zu verwenden, da es vom Benutzer manipuliert sein kann. Falls unter server_name mehrere Adressen eingetragen sind, wird der erste genommen.

/etc/nginx/sites-available/com.linuxserversetup.conf


# redirect to www
server {
  listen      80;
  server_name linuxserversetup.com;
  return      301 $scheme://www.$server_name$request_uri;
}

# main block
server {
  listen      80 default_server;
  listen      [::]:80;
  server_name www.linuxserversetup.com;

  root        /var/www/com.linuxserversetup/;
  index       index.htm;

  location / {
    try_files $uri $uri/ /index.htm;
  }

  location = /favicon.ico { access_log off; log_not_found off; }
}
 

Einen Ordner für das Web-Projekt legen wir ebenfalls an und benennen diesen entsprechend der Konvention com.linuxserversetup:


__$ mkdir /var/www/com.linuxserversetup
 

Vorerst platzieren wir dort auch eine index.htm. Das wird später, je nach Webtechnologie, geändert:


__$ nano /var/www/com.linuxserversetup/index.htm
 

Das kann auch eine leere HTML Seite sein. Einen Titel gebe ich zusätzlich an, damit lässt sie sich besser vom vorherigen Standard Server Block unterscheiden:

/var/www/com.linuxserversetup/index.htm


<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>linuxserversetup.com</title>
  </head>
  <body></body>
</html>
 

Jetzt erstellen wir noch die symbolische Verknüpfung in dem Ordner /etc/nginx/sites-enabled zu der Konfigurationsdatei com.linuxserversetup.conf mit dem Befehl ln:


__$ sudo ln -s /etc/nginx/sites-available/com.linuxserversetup.conf /etc/nginx/sites-enabled/
 

Abschließend überprüfen und starten wir NGINX neu, um die neue Konfiguration zu übernehmen:


__$ sudo nginx -t
__$ sudo systemctl restart nginx
 

Nginx sollte nun in der Lage sein zwischen IP und Domain Anfragen zu unterscheiden.

Testen können wir das mit einem Browser. Die Adresse http://116.203.69.89 sollte den "default" Title anzeigen. http://linuxserversetup.com und http://www.linuxserversetup.com zeigen einen anderen Titel.


Subdomain Server Block (dev)

Jedes Web-Projekt sollte eine Entwicklungs- oder Testseite haben. Die Subdomain dev hatten wir schon in dem Kapitel DNS Einträge vorbereitet.

Die Vorgehensweise ist identisch zu dem vorherigen Abschnitt, es ändern sich eigentlich nur die Bezeichnungen.

Die .conf Datei anlegen:


__$ sudo nano /etc/nginx/sites-available/com.linuxserversetup.dev.conf
 

Der Inhalt von com.linuxserversetup.dev.conf:

/etc/nginx/sites-available/com.linuxserversetup.dev.conf


server {
  listen      80;
  listen      [::]:80;
  server_name dev.linuxserversetup.com;

  root        /var/www/com.linuxserversetup.dev/;
  index       index.htm;

  location / {
    try_files $uri $uri/ /index.htm;
  }

  location = /favicon.ico { access_log off; log_not_found off; }
}
 

Den Ordner unter /var/www/ erstellen:


__$ mkdir /var/www/com.linuxserversetup.dev
 

Die index.htm anlegen:


__$ nano /var/www/com.linuxserversetup.dev/index.htm
 

Der Inhalt der index.htm:

/var/www/com.linuxserversetup.dev/index.htm


<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>dev.linuxserversetup.com</title>
  </head>
  <body></body>
</html>
 

Den symbolischen Link in dem Ordner /etc/nginx/sites-enabled erstellen:


__$ sudo ln -s /etc/nginx/sites-available/com.linuxserversetup.dev.conf /etc/nginx/sites-enabled/
 

Überprüfen und neu starten von NGINX:


__$ sudo nginx -t
__$ sudo systemctl restart nginx
 

Testen können wir die Adresse http://dev.linuxserversetup.com wieder mit dem Browser.

Später werden wir die Konfigurationsdateien weiter ausbauen und auf verschiedene Backends umleiten.

Als nächstes werden wir TLS Zertifikate einbinden und das HTTPS Protokoll erzwingen.