Backendy LXC po szyfrowanych łączach HTTPS
Tutorial jak zestawić witryny HTTPS na jednym VPS przy pomocy nginx i LXC.

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:
- Dodanie usera nginx
sudo useradd -s /sbin/nologin nginx
- Instalacja narzedzi budowania
sudo apt install build-essential -y
- 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
- 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
- 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"
- Budowanie NGINX
make
- Instalacja w systemie operacyjnym
make install
- 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 zmianiesudo 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:
- Dodanie usera nginx
sudo useradd -s /sbin/nologin nginx
- Instalacja narzedzi budowania
yum groupinstall "Development Tools"
- 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
- 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.gzcd ./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.gzcd ./openssl-1.1.0f
./configure
make
make install
- 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
- 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/
- Budowanie NGINX
make
- Instalacja w systemie operacyjnym
make install
- 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 zmianiesudo systemctl daemon-reload
- 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.