Backendy LXC po szyfrowanych łączach HTTPS

Tutorial jak zestawić witryny HTTPS na jednym VPS przy pomocy nginx i LXC.

Backendy LXC po szyfrowanych łączach HTTPS

Już grubo ponad rok temu doszedłem przy pomocy prób i błędów i poradników w internecie jak zrobić na jednym serwerze VPS wiele stron, a każda strona mająca własny oddzielny kubełek z samoistnym systemem operacyjnym przy pomocy LinuX Containers (LXC). To było nieodzowne hostując narażone na ataki strony wykonane w technologii Wordpress. Jednak już wtedy próbując, niestety nie udało mi się tego ustawić po HTTPsach. Można było to uczynić jednak wszystkie backendy LXC musiały mieć tylko jeden certyfikat - co oczywiście było błędne.

Poprzedni schemat (po HTTP) wyglądał słownie tak:

VPS z nginx -> LXC z nginx   -> LXC z WWW po HTTP na apache'u
             -> LXC z WWW po HTTP na nginx
             -> LXC z WWW po HTTP przez nginx -> serwisu na danym porcie (np. forum nodeBB)

Pomimo, że nie znalazłem kompleksowej podpowiedzi w internecie (na stackoverflow lub innym zaawansowanym blogu IT) i pomimo, że niby reverse proxy Traefik robi taką magię (w tym przypadku Traefik nie działał do końca, gdyż gubił backend domain na głębszych URI)... pomimo wielu prób i błędów, potrzeby kompilacji nginx i apache'a dociekań przez ponad 1 rok w tamtym tygodniu udało mi się!

W tym wpisie opiszę ktok po kroku jak to wszysto ustawić...

Przygotowanie nie będące częścią tego poradnika

Aby zabrać się za stawianie wielu stron WWW na jednym VPS tak aby każda strona stanowiła oddzielną hermetyczny system operacyjny.

Należy:

  • oczywiście nabyć VPS lub serwer BareMetal. Ja posiadam taki serwer w firmie Scaleway.com i mam dosyć silna maszynę:

C2L 8 x86-64 32 GB 50 GB/250 GB Direct SSD 600 Mbits/s 5 Gbit/s €23.99/mo

W chwili pisania tego artykułu na tej maszynie mam około 30 kontenerów LXC i jak nie ma ataku DDoS wszystko smiga i jest sporo wolnego RAMu.
W środku tygodnia rano zajętość serwera było np.:
25 GB RAMU wolne; 7% CPU zajęte; Load average: ~0,3; ~0.7; ~1,0; HDD: 92GB zajęte/44GB wolne

  • zainstalować oprogramowanie LXC w systemie operacyjnym hosta:

Polecam ten poradnik, jest dosyć precyzyjny i omawia sprawy również zaawansowane:

https://help.ubuntu.com/lts/serverguide/lxc.html

Można LXC zainstalować w trybie PRIVILEGED (wszystko jest na koncie ROOT) i UNPRIVILEGES (LXC dziala na innym koncie niż ROOT).
W zasadzie mam dwa serwery jednym LXC jest na ROOT drugi udało mi się skonfigurować na koncie NIE ROOT. W tym drugim prypadku troszkę jest więcej konfiguracji i kilka przeszdków do pokonania, ale da się. Z drugiej strony ten serwer z LXC po ROOT dziala bez awarii już prawie 2 lata. Czyli wybór pozostawiam Tobie, jaki tryb LXC wybrać...

  • zainstalować kontener LXC pod 1-wszy backend według powyższego poradnika Ubuntu.

uwaga:
Ja na hostach mam Ubuntu 16.04 LTS, jak i kontenery mam na Ubuntu 16.04 LTS - dobrze się sprawdzały, jednak po najnowszej aktualizacji wew. ubuntu 16.04, z dziwnych powodów nie chiała mi się zainicjalizować baza danych MySQL i jest to wina na styku UBUNTU -> LXC -> UBUNTU -> MySQL. Błąd był jakiś uprawnień i jakis slice w systemd nie działał. Zamieniłem kontener z Ubuntu 16.04 na najnowszy Centos 7 i błąd inicjalizacji bazy danych MySQL nie wystąpił.

Kompilacja najnowszego NGINX na OS hosta i gościa:

Kompilacja na systemie Ubuntu 16.04:

  1. Dodanie usera nginx sudo useradd -s /sbin/nologin nginx
  2. Instalacja narzedzi budowania sudo apt install build-essential -y
  3. Instalacja niezbędnych do kompilacji pakietów sudo apt install libpcre3-dev openssl libssl-dev libxslt-dev libgd-dev geoip-bin geoip-database libgeoip-dev google-perftools libgoogle-perftools-dev libatomic-ops-dev libperl-dev -y
  4. Pobranie najnowszej, stabilnej wersji NGINX, rozpakowanie i wejście do katalogu NGINX curl -O https://nginx.org/download/nginx-1.14.0.tar.gz tar xvf nginx-1.14.0.tar.gz cd ./nginx-1.14.0
  5. Pre-Konfiguracja NGINX i wszystkim modułów które będą potrzebne: sudo ./configure --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --with-select_module --with-poll_module --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_xslt_module=dynamic --with-http_image_filter_module --with-http_image_filter_module=dynamic --with-http_geoip_module --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module --with-http_perl_module=dynamic --with-mail --with-mail=dynamic --with-mail_ssl_module --with-stream --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module --with-stream_geoip_module=dynamic --with-stream_ssl_preread_module --with-google_perftools_module --with-cpp_test_module --with-compat --with-pcre --with-pcre-jit --with-zlib-asm=CPU --with-libatomic --with-debug --with-ld-opt="-Wl,-E"
  6. Budowanie NGINX make
  7. Instalacja w systemie operacyjnym make install
  8. Tworzenie serwisu z NGINX sudo vim /etc/systemd/system/nginx.service paste:
    NOTE: The location of the PID file and the NGINX binary may be different depending on how NGINX was compiled.
[Unit]
Description=A high performance web server and a reverse proxy server
After=network.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /usr/local/nginx/logs/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

Przeładowanie serwisów po zmianie
sudo systemctl daemon-reload

9. Uruchomienie NGINX i włączenie go po rebootach systemu sudo systemctl start nginx.service && sudo systemctl enable nginx.service

UWAGA: Od jakiegoś czasu można zainstalować wersję NGINX 1.14+ przy pomocy pakietów z repo NGINX'a w Ubuntu np. 16.04 i 18.04.
W tym celu należy wykonać poniższe komendy:

sudo apt install software-properties-common

sudo add-apt-repository ppa:nginx/stable

sudo apt update

sudo apt install nginx

Kompilacja na systemie Centos 7 jako root:

  1. Dodanie usera nginx sudo useradd -s /sbin/nologin nginx
  2. Instalacja narzedzi budowania yum groupinstall "Development Tools"
  3. Instalacja niezbędnych do kompilacji pakietów yum install -y epel-release yum install -y perl perl-devel perl-ExtUtils-Embed libxslt libxslt-devel libxml2 libxml2-devel gd gd-devel GeoIP GeoIP-devel google-perftools-devel libatomic_ops-devel
  4. Pobranie potrzebnych bibliotek i kompilacja każdej z nich:

PCRE version 8.40

wget https://ftp.pcre.org/pub/pcre/pcre-8.40.tar.gz && tar xzvf pcre-8.40.tar.gz
cd ./pcre-8.40
./configure
make
make install

zlib version 1.2.11

wget https://www.zlib.net/zlib-1.2.11.tar.gz && tar xzvf zlib-1.2.11.tar.gz
cd ./zlib-1.2.11
./configure
make
make install

OpenSSL version 1.1.0f

wget https://www.openssl.org/source/openssl-1.1.0f.tar.gz && tar xzvf openssl-1.1.0f.tar.gz
cd ./openssl-1.1.0f
./configure
make
make install

  1. Pobranie najnowszej, stabilnej wersji NGINX, rozpakowanie i wejście do katalogu NGINX curl -O https://nginx.org/download/nginx-1.14.0.tar.gz tar xvf nginx-1.14.0.tar.gz cd ./nginx-1.14.0
  2. Pre-Konfiguracja NGINX i wszystkim modułów które będą potrzebne: ./configure --user=nginx --group=nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --with-select_module --with-poll_module --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_xslt_module=dynamic --with-http_image_filter_module --with-http_image_filter_module=dynamic --with-http_geoip_module --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module --with-http_perl_module=dynamic --with-mail --with-mail=dynamic --with-mail_ssl_module --with-stream --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_geoip_module --with-stream_geoip_module=dynamic --with-stream_ssl_preread_module --with-google_perftools_module --with-cpp_test_module --with-compat --with-pcre --with-pcre-jit --with-zlib-asm=CPU --with-libatomic --with-debug --with-ld-opt="-Wl,-E" --with-openssl=./openssl-1.1.0f/
  3. Budowanie NGINX make
  4. Instalacja w systemie operacyjnym make install
  5. Tworzenie serwisu z NGINX sudo vim /usr/lib/systemd/system/nginx.service

wklejamy:

    NOTE: The location of the PID file and the NGINX binary may be different depending on how NGINX was compiled.

[Unit]
Description=nginx - high performance web server
Documentation=https://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -c /etc/nginx/nginx.conf
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

Przeładowanie serwisów po zmianie
sudo systemctl daemon-reload

  1. Uruchomienie NGINX i włączenie go po rebootach systemu sudo systemctl start nginx.service && sudo systemctl enable nginx.service

Kompilacja najnowszego APACHE na OS gościa:

-- TODO

Jednak dla Ubuntu jest możliwość instalacji w miarę nowego Apache2 z repo ondrej, jak poniżej:

apt-get install software-properties-common

add-apt-repository ppa:ondrej/apache2
apt-get update

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 4F4EA0AAE5267A6C

apt-get update

apt-get dist-upgrade

Konfiguracja NGINX hosta

W /etc/nginx/nginx.conf definiujemy jak poniżej na przykład:

stream {
    map $ssl_preread_server_name $name {
        domena1 https_domena1;
        domena2 https_domena2;
        domena3 https_domena3;
        default https_default;
    }

        log_format log_stream '$remote_addr [$time_local] $protocol [$ssl_preread_server_name] [$name] $status $bytes_sent $bytes_received $session_time';


        access_log /var/log/nginx/access-stream.log log_stream;
        error_log  /var/log/nginx/error-stream.log;

    upstream https_domena1 {
        server 10.0.3.153:443; #gdzie 10.0.3.153 to IP wew. kontenera LXC z serwisem dla domeny1
    }

    upstream https_domena2 {
        server 10.0.3.253:443;
    }

    upstream https_domena2 {
        server 10.0.3.223:443;
    }

    upstream https_default {
        server 10.0.3.1:443; #IP wskazujący na nieistniejący kontener
    }


    server {
        listen 10.1.71.117:443; #IP interfejsu hosta i port HTTPS
        proxy_pass $name;
        ssl_preread on;
		proxy_protocol on;
	}
}                                                                                                                    116,9         75%

Konfiguracja NGINX gościa (kontenera LXC)

W pliku /etc/nginx/nginx.conf definiujemy jak poniżej dla przykładu:

load_module modules/ngx_stream_module.so;
user  nginx;
worker_processes  1;

error_log  /var/log/nginx-error.log;
error_log  /var/log/nginx-error.log  notice;
error_log  /var/log/nginx-error.log  info;

events {
    worker_connections  1024;
}



http {
    include       mime.types;
    default_type  application/octet-stream;


    log_format main '$http_x_forwarded_for - $remote_user [$time_local] '
	'"$request" $status $body_bytes_sent "$http_referer" '
	'"$http_user_agent" $remote_addr' ;

	log_format specialLog '$remote_addr forwarded for $http_x_real_ip - $remote_user [$time_local]  '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent"';

    access_log /var/log/nginx-access-special.log specialLog;
    access_log /var/log/nginx-access.log;
    error_log /var/log/nginx-error.log;


    access_log  /var/log/nginx-access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    #access_log  /var/logs/nginx-.access.log  main;

	### redirects http requests to https
	server {
    listen 80;
    server_name domenaX;

    return 302 https://$server_name$request_uri;
}

### the https server
server {
    # listen on ssl, deliver with speedy if possible
    listen 443 proxy_protocol ssl;

    server_name domenaX;

    # change these paths!
    ssl_certificate /etc/nginx/certs/cert1.pem;
    ssl_certificate_key /etc/nginx/certs/privkey1.pem;

    # enables all versions of TLS, but not SSLv2 or 3 which are weak and now deprecated.
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # disables all weak ciphers
	#    ssl_ciphers 'AES128+EECDH:AES128+EDH';
	#    ssl_prefer_server_ciphers on;

    location / {

        real_ip_header    proxy_protocol;
        real_ip_recursive on;
#       set_real_ip_from   10.0.3.0/24;


        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proy_set_header X-NginX-Proxy true;

		# This is for websockets - example for nodeBB service
        proxy_pass http://127.0.0.1:4567;  # no trailing slash
        proxy_redirect off;
        # Socket.IO Support
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Konfiguracja APACHE2 (musi być koniecznie wersja minimum 2.4.31+) gościa (kontenera LXC)

Konfiguracja logów w pliku /etc/apache2/apache2.conf

LogFormat "%v:%p %a %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

W conf-enabled w plikach o dowolnej nazwie należy umieścić:

Odnośnie logów

CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined

Odnośnie wykrywania w logacj true remote client IP:

RemoteIPHeader  X-Forwarded-For
RemoteIPTrustedProxy 10.0.3.0/24 #gdzie tutaj wpisujemy podsieć kontenerów LXC

W sites-enabled w pliku o dowolnej nazwie należy umieścić definicję serwisu SSL:

LoadModule ssl_module modules/mod_ssl.so

<VirtualHost *:443>
    ServerName domainX
    SSLEngine on
    SSLCertificateFile "/etc/apache2/sites-certs/cert1.pem"
    SSLCertificateKeyFile "/etc/apache2/sites-certs/privkey1.pem"

    RemoteIPProxyProtocol On
    RemoteIPProxyProtocolExceptions 0.0.0.0
</VirtualHost>

A powyższa konfiguracja zadziała dopiero, gdy modul Apache 'remoteip' będzie zainstalowany i załadowany.