Devlog: zaplecze techniczne i zmiany na stronie

Witaj drogi Czytelniku!

To mój pierwszy wpis typu Devlog, w którym będę opisywał zmiany zachodzące na stronie – zarówno te techniczne, jak i dotyczące całej infrastruktury.

Czas ograniczyć koszty

Pierwszą rzeczą, którą postanowiłem zrobić, było ograniczenie wydatków. Utrzymywanie kilku serwerów VPS i wydawanie kilkuset złotych rocznie zaczęło mijać się z celem. Stwierdziłem więc, że warto zrobić porządki – wyrzucić zbędne usługi i zostawić tylko to, co naprawdę potrzebne.

Obecnie infrastruktura wygląda następująco:

  • VPS od RackNerd – około 50 zł rocznie,
  • VPS Mikr.us Frog – jednorazowe 5 zł,
  • Zoho Mail – około 60zł rocznie,
  • VPS od Mikr.us – dostałem go w ramach pisania notek,
  • oraz TrueNAS hostowany u liske1 🙂
Do czego służą poszczególne serwery?

RackNerd VPS – główny VPS

Serwer VPS od RackNerd pełni rolę hostingu mojego bloga – na którego obecnie patrzysz 🙂

Największą zaletą VPS z RackNerd i głównym powodem, dla którego wybrałem właśnie tę usługę, jest dostęp do publicznego adresu IPv4 w bardzo niskiej cenie. Obecnie coraz trudniej znaleźć sensownego VPS z IPv4 bez dodatkowych opłat, dlatego była to dla mnie istotna przewaga.

Mikr.us Frog

Mały VPS z Mikr.us Frog działa jako serwer SOCKS5 Proxy.
Używam go głównie do tunelowania SSH i wystawienia portu SOCKS5 dla skryptów BASH uruchamianych na głównym serwerze.

W tym przypadku kluczową rolę przy wyborze odegrał model rozliczenia – jednorazowa opłata aktywacyjna w wysokości 5 zł była na tyle niska, że praktycznie przesądziła o decyzji, niezależnie od pozostałych parametrów.

Mikr.us

Mały VPS z Mikr.us jest głównie do pisania notek, oraz host dzięki któremu mam usługę Cytr.us.
Wcześniej był na nim zainstalowany Debian, jednak w celu ujednolicenia infrastruktury został zastąpiony przez Alpine.

Ten VPS, podobnie jak VPS z Mikr.us Frog, nie był serwerem, który brałem pod uwagę jako serwer główny. Powodem takiej decyzji był brak pełnego publicznego adresu IPv4, co w moim przypadku stanowiło wymóg nie do przeskoczenia. Istotny był również brak wirtualizacji KVM – byłem zdany na gotowe obrazy systemów i nie miałem możliwości samodzielnej instalacji systemu od podstaw z poziomu recovery console.

Możliwość rozbudowy infrastruktury

Gdyby pojawiła się potrzeba uruchomienia kolejnego serwera VPS, mam jeszcze jedną sensowną opcję w zanadrzu – TierHive. Oczywiście nie odrzucam również Mikr.us jako serwera, który mógłby zrealizować jakiś mój inny pomysł w przyszłości.

Na szczęście obecnie staram się bardziej upraszczać infrastrukturę niż ją rozbudowywać 😉

Jak to wszystko działa?

Dystrybucja

Wszystkie moje serwery stoją na Alpine Linux . Krok ten został podyktowany tym, że miałem dość wszystkiego co oparte o systemd, a także potrzebowałem czegoś szybkiego, bez zbędnego bloatware. To tak jak zapytać użytkownika Void Linux bądź Devuan dlaczego wybrał akurat tą dystrybucję.

Firewall

Na serwerze głównym został wygenerowany klucz SSH, którego klucz publiczny został przesłany na serwer Frog. Dzięki temu działa usługa odpowiedzialna za wystawienie tunelu SOCKS5. Przez ten tunel łączy się skrypt pobierający dane z czarnej listy prowadzonej przez Jakuba Mrugalskiego.

Poniżej znajduje się konfiguracja tej usługi odpowiedzialnej za to zadanie (/etc/init.d/ssh-socks):

#!/sbin/openrc-run

name="SSH SOCKS5 tunnel"
description="Persistent SSH SOCKS5 tunnel"

command="/usr/bin/ssh"

command_args="
    -N
    -D 1080
    -p 10105
    -o ExitOnForwardFailure=yes
    -o ServerAliveInterval=30
    -o ServerAliveCountMax=3
    [email protected]
"

command_background="true"
pidfile="/run/${RC_SVCNAME}.pid"

supervisor="supervise-daemon"

depend() {
    need net
}

Każdego dnia o godzinie 05:00 uruchamiany jest skrypt run_firewall.sh, którego zadaniem jest pobranie adresów IP z powyżej wspomnianej czarnej listy, a następnie dodanie ich do zestawu ipset. Tak przygotowana lista jest później wykorzystywana do blokowania ruchu na serwerze S2.

Poniżej efekt działania skryptu (plik auth.log).

Przed:

Po:

Uwaga: Mam świadomość, że na załączonych zrzutach ekranu widoczne są różne nazwy hostów. Wynika to z faktu, że pochodzą one z dwóch różnych etapów życia tego samego serwera. Historycznie serwer funkcjonował pod nazwą „S2” i był oparty na systemie Ubuntu. Obecnie ten sam serwer działa pod hostname fx.vc-mp.eu oraz został zmigrowany do systemu Alpine Linux.

Strona internetowa – blog

Cała instalacja serwera LAMP została wgrana z pomocą ChatGPT – tak, dobrze czytasz, korzystam z AI. W praktyce posłużyłem się nim jako wsparciem przy konfiguracji MariaDB, Apache2 oraz PHP-FPM, w tym doborze ich parametrów i optymalizacji pod mój przypadek.

Przykładowa konfiguracja:

/etc/apache2/conf.d/mpm.conf

<IfModule mpm_event_module>
    StartServers        3

    ServerLimit              8
    ThreadsPerChild          25

    MinSpareThreads          25
    MaxSpareThreads          75

    MaxRequestWorkers   50
    MaxConnectionsPerChild      200

    KeepAlive On
    KeepAliveTimeout 2
    MaxKeepAliveRequests 100
</IfModule>

/etc/apache2/conf.d/php84-module.conf

LoadModule php_module modules/mod_php84.so

DirectoryIndex index.php index.html

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

/etc/my.cnf.d/server.cnf

[mysqld]

innodb_buffer_pool_size = 256M
innodb_buffer_pool_instances = 1
innodb_log_file_size = 64M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2

max_connections = 30
thread_cache_size = 8
table_open_cache = 512

tmp_table_size = 32M
max_heap_table_size = 32M

performance_schema = OFF
skip-name-resolve

/etc/php84/php.ini (wyłącznie ważniejsze wartości)

memory_limit = 128M
post_max_size = 32M
upload_max_filesize = 20M

Gotowy skrypt instalujący LAMP na Alpine i go wstępnie konfigurujący dla serwerów Mikr.us (minimalna oferta to: 2.1)

#!/bin/bash

CRON_JOB='0 3 * * * /usr/local/bin/update-sudo-rs.sh >> /var/log/update-sudo-rs.log 2>&1'

cat > /etc/apk/repositories << 'EOF'
https://alpine.sakamoto.pl/alpine/v3.23/main
https://alpine.sakamoto.pl/alpine/v3.23/community
EOF

apk update
apk upgrade
apk add apache2 apache2-ssl apache2-proxy apache2-proxy-html zip unzip php84 php-fpm php-mysqli php-pdo php-pdo_mysql php-session php-json php-mbstring php-openssl php-curl php-xml php-dom php-ctype php-iconv php-simplexml php-redis redis mariadb mariadb-client
apk add linux-pam
apk del sudo
apk add sudo-rs --repository=https://alpine.sakamoto.pl/alpine/edge/community

sed -i 's|listen = 127.0.0.1:9000|listen = /run/php-fpm84/php-fpm.sock|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|;listen.owner = nobody|listen.owner = apache|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|;listen.group = nobody|listen.group = apache|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|;listen.mode = 0660|listen.mode = 0660|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|pm = dynamic|pm = ondemand|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|pm.max_children = 5|pm.max_children = 3|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|;pm.process_idle_timeout = 10s|pm.process_idle_timeout = 10s|g' /etc/php84/php-fpm.d/www.conf
sed -i 's|#LoadModule proxy_module|LoadModule proxy_module|g' /etc/apache2/httpd.conf
sed -i 's|#LoadModule proxy_fcgi_module|LoadModule proxy_fcgi_module|g' /etc/apache2/httpd.conf
sed -i 's|Listen 80|Listen 0.0.0.0:80\nListen [::]:80|g' /etc/apache2/httpd.conf
sed -i -e 's|^#\?bind .*|bind 127.0.0.1|' /etc/redis.conf

cat >> /etc/apache2/conf.d/php-fpm.conf << 'EOF'
<FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php-fpm84/php-fpm.sock|fcgi://localhost"
</FilesMatch>
DirectoryIndex index.php index.html
EOF

cat > /etc/php84/conf.d/00_opcache.ini << 'EOF'
zend_extension=opcache
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.save_comments=1
opcache.fast_shutdown=1
EOF

rc-service mariadb setup
cat >> /etc/my.cnf.d/low-memory.cnf << 'EOF'
[mysqld]
performance_schema = OFF
innodb_buffer_pool_size = 192M
innodb_log_buffer_size = 2M
query_cache_size = 0
query_cache_type = 0
max_connections = 20
innodb_buffer_pool_instances = 1
innodb_log_file_size = 64M
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 2
thread_cache_size = 8
table_open_cache = 512
tmp_table_size = 32M
max_heap_table_size = 32M
skip-name-resolve
EOF

rc-update add mariadb default
rc-update add php-fpm84 default
rc-update add apache2 default
rc-update add redis default

cat > /usr/local/bin/update-sudo-rs.sh <<'EOF'
#!/bin/sh

apk update \
  --repository=https://alpine.sakamoto.pl/alpine/edge/community

apk upgrade -U sudo-rs \
  --repository=https://alpine.sakamoto.pl/alpine/edge/community
EOF

chmod +x /usr/local/bin/update-sudo-rs.sh
grep -Fxq "$CRON_JOB" /etc/crontabs/root || echo "$CRON_JOB" >> /etc/crontabs/root

reboot now
Galeria zdjęć

Na TrueNAS uruchomiona jest aplikacja hostująca moją galerię zdjęć. Każde jedno wykonane zdjęcie telefonem przesyłane jest automatycznie do tej aplikacji dzięki czemu mam backup zdjęć i mogę je przeglądać na wielu urządzeniach.

Dzięki serwerowi od RackNerd i dostępnemu publicznemu adresowi IPv4 mogę na domowym serwerze DNS przypisać ten adres do losowej nazwy hosta. W praktyce pozwala mi to wystawić galerię zdjęć w internecie, mimo że fizycznie działa ona w mojej domowej infrastrukturze. W celu zapewnienia bezpiecznego połączenia utworzyłem własny urząd certyfikacji (CA), a następnie wygenerowałem certyfikat SSL wraz z odpowiadającym mu kluczem prywatnym. Certyfikat główny CA został następnie zainstalowany na wszystkich urządzeniach korzystających z usługi, dzięki czemu połączenie jest rozpoznawane jako zaufane.

Dostęp do galerii nie jest wprost publiczny – wymaga użycia skanera lub znajomości sposobu, w jaki została wystawiona. Taki bardziej złożony model dostępu jest celowy i wynika ze względów bezpieczeństwa, aby ograniczyć możliwość przypadkowego odnalezienia usługi.

Całość działa więc jako połączenie:

  • lokalnego hostingu na TrueNAS,
  • publicznego IPv4 z RackNerd,
  • oraz własnej warstwy DNS, która spina to w jedną całość.

W praktyce jest to w pełni produkcyjne środowisko, tylko zaprojektowane w sposób świadomie utrudniający jego odkrycie i dostęp z zewnątrz.

Przywrócenie podstrony z narzędziami

Ten punkt to bardziej chęć pochwalenia się tym, czego używam na co dzień. Skoro inni prezentują swoje narzędzia i workflow, to postanowiłem również pokazać swoje rozwiązania oraz aplikacje, z których korzystam.

Usunięcie podstrony kontaktowej

Podstrona kontaktowa została tymczasowo usunięta ze względów bezpieczeństwa. Wcześniej korzystałem z wtyczki napisanej przez Beherita, jednak z powodu braku jej dalszego utrzymywania uznałem, że najlepszym rozwiązaniem będzie całkowite usunięcie formularza kontaktowego.

Podsumowanie

Udało się zakończyć migrację, uporządkować konfigurację usług i doprowadzić środowisko do stabilnego stanu. W następnych notkach spróbuję opisać moje kolejne eksperymenty z infrastrukturą, nowe wdrożenia, a może i jakiś poradnik – jeżeli będzie taka potrzeba.

5 1 głos
Ocena artykułu
Subskrybuj
Powiadom o
guest

0 Komentarze
Najstarsze
Najnowsze Najwięcej głosów
0
Chętnie poznam Twoje przemyślenia, skomentuj.x