Workshop RHEL 8 Beta: Vytváranie fungujúcich webových aplikácií

RHEL 8 Beta ponúka vývojárom mnoho nových funkcií, ktorých zoznam by mohol zabrať strán, avšak učiť sa nové veci je v praxi vždy lepšie, preto nižšie ponúkame workshop o skutočnom vytvorení aplikačnej infraštruktúry založenej na Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Vytváranie fungujúcich webových aplikácií

Zoberme si Python, medzi vývojármi obľúbený programovací jazyk, ako základ kombináciu Django a PostgreSQL, pomerne bežnú kombináciu na tvorbu aplikácií, a nakonfigurujme RHEL 8 Beta na prácu s nimi. Potom pridáme ešte pár (nezaradených) ingrediencií.

Testovacie prostredie sa zmení, pretože je zaujímavé skúmať možnosti automatizácie, práce s kontajnermi a skúšať prostredia s viacerými servermi. Ak chcete začať s novým projektom, môžete začať ručným vytvorením malého jednoduchého prototypu, aby ste presne videli, čo sa musí stať a ako to spolupôsobí, a potom prejsť k automatizácii a vytváraniu zložitejších konfigurácií. Dnes hovoríme o vytvorení takéhoto prototypu.

Začnime nasadením obrazu virtuálneho počítača RHEL 8 Beta. Virtuálny počítač môžete nainštalovať úplne od začiatku alebo použiť hosťovský obraz KVM dostupný s vaším predplatným Beta. Keď používate hosťovský obraz, budete musieť nakonfigurovať virtuálne CD, ktoré bude obsahovať metaúdaje a používateľské údaje na cloudovú inicializáciu (cloud-init). So štruktúrou disku alebo dostupnými balíčkami nemusíte robiť nič špeciálne, postačí akákoľvek konfigurácia.

Pozrime sa na celý proces bližšie.

Inštalácia Django

S najnovšou verziou Django budete potrebovať virtuálne prostredie (virtualenv) s Pythonom 3.5 alebo novším. V poznámkach k beta verzii môžete vidieť, že Python 3.6 je k dispozícii, skontrolujme, či je to skutočne tak:

[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found

Red Hat aktívne používa Python ako systémovú súpravu nástrojov v RHEL, tak prečo je to výsledok?

Faktom je, že veľa vývojárov Pythonu stále uvažuje o prechode z Pythonu 2 na Python 2, zatiaľ čo samotný Python 3 je v aktívnom vývoji a neustále sa objavujú nové a nové verzie. Preto, aby sme uspokojili potrebu stabilných systémových nástrojov a zároveň ponúkli používateľom prístup k rôznym novým verziám Pythonu, systémový Python bol presunutý do nového balíka a poskytol možnosť nainštalovať Python 2.7 aj 3.6. Viac informácií o zmenách a dôvodoch ich vykonania nájdete v publikácii v Blog Langdona Whitea (Langdon White).

Na sfunkčnenie Pythonu vám teda stačí nainštalovať dva balíčky, pričom ako závislosť je zahrnutý python3-pip.

sudo yum install python36 python3-virtualenv

Prečo nepoužiť priame volania modulov, ako navrhuje Langdon, a nenainštalovať pip3? S ohľadom na nadchádzajúcu automatizáciu vieme, že Ansible bude vyžadovať nainštalovaný pip na spustenie, pretože modul pip nepodporuje virtualenvs s vlastným spustiteľným súborom pip.

S funkčným interpretom python3, ktorý máte k dispozícii, môžete pokračovať v procese inštalácie Django a mať funkčný systém spolu s našimi ďalšími komponentmi. Na internete je veľa možností implementácie. Je tu prezentovaná jedna verzia, ale používatelia môžu použiť svoje vlastné procesy.

Verzie PostgreSQL a Nginx dostupné v RHEL 8 nainštalujeme štandardne pomocou Yum.

sudo yum install nginx postgresql-server

PostgreSQL bude vyžadovať psycopg2, ale musí byť dostupný iba v prostredí virtualenv, takže ho nainštalujeme pomocou pip3 spolu s Django a Gunicorn. Najprv však musíme nastaviť virtualenv.

Na tému výberu správneho miesta na inštaláciu projektov Django je vždy veľa debát, no v prípade pochybností sa vždy môžete obrátiť na Linux Filesystem Hierarchy Standard. FHS konkrétne hovorí, že /srv sa používa na: „ukladanie údajov špecifických pre hostiteľa – údajov, ktoré systém vytvára, ako sú údaje a skripty webového servera, údaje uložené na serveroch FTP a riadenie systémových úložísk.“ verzie (objavené vo FHS -2.3 v roku 2004).

Toto je presne náš prípad, takže všetko potrebné vložíme do /srv, ktorý vlastní používateľ našej aplikácie (cloud-user).

sudo mkdir /srv/djangoapp
sudo chown cloud-user:cloud-user /srv/djangoapp
cd /srv/djangoapp
virtualenv django
source django/bin/activate
pip3 install django gunicorn psycopg2
./django-admin startproject djangoapp /srv/djangoapp

Nastavenie PostgreSQL a Django je jednoduché: vytvorte databázu, vytvorte používateľa, nakonfigurujte povolenia. Jedna vec, ktorú treba mať na pamäti pri počiatočnej inštalácii PostgreSQL, je skript postgresql-setup, ktorý sa inštaluje s balíkom postgresql-server. Tento skript vám pomáha vykonávať základné úlohy spojené so správou databázového klastra, ako je inicializácia klastra alebo proces aktualizácie. Ak chcete nakonfigurovať novú inštanciu PostgreSQL v systéme RHEL, musíme spustiť príkaz:

sudo /usr/bin/postgresql-setup -initdb

Potom môžete spustiť PostgreSQL pomocou systemd, vytvoriť databázu a nastaviť projekt v Django. Nezabudnite reštartovať PostgreSQL po vykonaní zmien v konfiguračnom súbore autentifikácie klienta (zvyčajne pg_hba.conf), aby ste nakonfigurovali ukladanie hesiel pre používateľa aplikácie. Ak narazíte na iné problémy, nezabudnite zmeniť nastavenia IPv4 a IPv6 v súbore pg_hba.conf.

systemctl enable -now postgresql

sudo -u postgres psql
postgres=# create database djangoapp;
postgres=# create user djangouser with password 'qwer4321';
postgres=# alter role djangouser set client_encoding to 'utf8';
postgres=# alter role djangouser set default_transaction_isolation to 'read committed';
postgres=# alter role djangouser set timezone to 'utc';
postgres=# grant all on DATABASE djangoapp to djangouser;
postgres=# q

V súbore /var/lib/pgsql/data/pg_hba.conf:

# IPv4 local connections:
host    all        all 0.0.0.0/0                md5
# IPv6 local connections:
host    all        all ::1/128                 md5

V súbore /srv/djangoapp/settings.py:

# Database
DATABASES = {
   'default': {
       'ENGINE': 'django.db.backends.postgresql_psycopg2',
       'NAME': '{{ db_name }}',
       'USER': '{{ db_user }}',
       'PASSWORD': '{{ db_password }}',
       'HOST': '{{ db_host }}',
   }
}

Po nakonfigurovaní súboru settings.py v projekte a nastavení konfigurácie databázy môžete spustiť vývojový server, aby ste sa uistili, že všetko funguje. Po spustení vývojového servera je dobré vytvoriť administrátora, aby ste otestovali pripojenie k databáze.

./manage.py runserver 0.0.0.0:8000
./manage.py createsuperuser

WSGI? Wai?

Vývojový server je užitočný na testovanie, ale na spustenie aplikácie musíte nakonfigurovať príslušný server a proxy pre rozhranie Web Server Gateway Interface (WSGI). Existuje niekoľko bežných kombinácií, napríklad Apache HTTPD s uWSGI alebo Nginx s Gunicornom.

Úlohou rozhrania Web Server Gateway Interface je posielať požiadavky z webového servera do webového rámca Python. WSGI je pozostatkom hroznej minulosti, keď existovali motory CGI, a dnes je WSGI de facto štandardom bez ohľadu na použitý webový server alebo framework Python. Ale napriek jeho širokému používaniu stále existuje veľa nuancií pri práci s týmito rámcami a veľa možností. V tomto prípade sa pokúsime nadviazať interakciu medzi Gunicorn a Nginx cez zásuvku.

Keďže oba tieto komponenty sú nainštalované na rovnakom serveri, skúsme namiesto sieťového soketu použiť soket UNIX. Keďže komunikácia v každom prípade vyžaduje socket, skúsme urobiť ešte jeden krok a nakonfigurovať aktiváciu socketu pre Gunicorn cez systemd.

Proces vytvárania soketových aktivovaných služieb je pomerne jednoduchý. Najprv sa vytvorí súbor jednotiek, ktorý obsahuje direktívu ListenStream ukazujúcu na bod, v ktorom sa vytvorí UNIXový soket, potom jednotkový súbor pre službu, v ktorej bude direktíva Requires ukazovať na súbor soketovej jednotky. Potom v súbore servisnej jednotky už zostáva len zavolať Gunicorn z virtuálneho prostredia a vytvoriť väzbu WSGI pre UNIX socket a aplikáciu Django.

Tu je niekoľko príkladov súborov jednotiek, ktoré môžete použiť ako základ. Najprv nastavíme zásuvku.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Teraz musíte nakonfigurovať démona Gunicorn.

[Unit]
Description=Gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=cloud-user
Group=cloud-user
WorkingDirectory=/srv/djangoapp

ExecStart=/srv/djangoapp/django/bin/gunicorn 
         —access-logfile - 
         —workers 3 
         —bind unix:gunicorn.sock djangoapp.wsgi

[Install]
WantedBy=multi-user.target

Pre Nginx je to jednoduchá záležitosť vytvorenia konfiguračných súborov proxy a nastavenia adresára na ukladanie statického obsahu, ak ho používate. V RHEL sú konfiguračné súbory Nginx umiestnené v /etc/nginx/conf.d. Nasledujúci príklad môžete skopírovať do súboru /etc/nginx/conf.d/default.conf a spustiť službu. Uistite sa, že ste nastavili názov_servera tak, aby sa zhodoval s názvom hostiteľa.

server {
   listen 80;
   server_name 8beta1.example.com;

   location = /favicon.ico { access_log off; log_not_found off; }
   location /static/ {
       root /srv/djangoapp;
   }

   location / {
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_pass http://unix:/run/gunicorn.sock;
   }
}

Spustite soket Gunicorn a Nginx pomocou systemd a ste pripravení začať testovať.

Chyba zlej brány?

Ak zadáte adresu do prehliadača, s najväčšou pravdepodobnosťou dostanete chybu 502 Bad Gateway. Môže to byť spôsobené nesprávne nakonfigurovanými povoleniami soketu UNIX alebo to môže byť spôsobené zložitejšími problémami súvisiacimi s riadením prístupu v SELinux.

V protokole chýb nginx môžete vidieť takýto riadok:

2018/12/18 15:38:03 [crit] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Permission denied) while connecting to upstream, client: 192.168.122.1, server: 8beta1.example.com, request: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com"

Ak otestujeme priamo Gunicorn, dostaneme prázdnu odpoveď.

curl —unix-socket /run/gunicorn.sock 8beta1.example.com

Poďme zistiť, prečo sa to deje. Ak otvoríte denník, s najväčšou pravdepodobnosťou uvidíte, že problém súvisí so SELinuxom. Keďže spúšťame démona, pre ktorý nebola vytvorená žiadna politika, je označený ako init_t. Poďme si túto teóriu vyskúšať v praxi.

sudo setenforce 0

To všetko môže spôsobiť kritiku a slzy krvi, ale to je len ladenie prototypu. Zakážme kontrolu, aby sme sa uistili, že ide o problém, potom všetko vrátime na svoje miesto.

Obnovením stránky v prehliadači alebo opätovným spustením nášho príkazu curl môžete zobraziť testovaciu stránku Django.

Takže keď sme sa uistili, že všetko funguje a už nie sú žiadne problémy s povoleniami, znova povolíme SELinux.

sudo setenforce 1

Nehovorí sa tu o audit2allow alebo vytváraní politík založených na výstrahách pomocou sepolgenu, keďže v súčasnosti neexistuje žiadna skutočná aplikácia Django, takže neexistuje úplná mapa toho, k čomu by Gunicorn mohol chcieť pristupovať a k čomu by mal prístup zakázať. Preto je potrebné ponechať SELinux v chode, aby bol systém chránený, a zároveň umožniť spustenie aplikácie a nechať správy v protokole auditu, aby sa z nich potom dala vytvoriť skutočná politika.

Určenie permisívnych domén

Nie každý počul o povolených doménach v SELinuxe, ale nie sú žiadnou novinkou. Mnohí s nimi dokonca pracovali bez toho, aby si to uvedomovali. Keď je politika vytvorená na základe auditových správ, vytvorená politika predstavuje vyriešenú doménu. Pokúsme sa vytvoriť jednoduchú politiku povoľovania.

Ak chcete vytvoriť špecifickú povolenú doménu pre Gunicorn, potrebujete nejaký druh politiky a tiež musíte označiť príslušné súbory. Okrem toho sú potrebné nástroje na zostavenie nových politík.

sudo yum install selinux-policy-devel

Mechanizmus povolených domén je skvelý nástroj na identifikáciu problémov, najmä pokiaľ ide o vlastnú aplikáciu alebo aplikácie, ktoré sa dodávajú bez už vytvorených politík. V tomto prípade bude povolená doménová politika pre Gunicorn čo najjednoduchšia – deklarujte hlavný typ (gunicorn_t), deklarujte typ, ktorý použijeme na označenie viacerých spustiteľných súborov (gunicorn_exec_t), a potom nastavte prechod pre systém, aby správne označoval bežiace procesy. Posledný riadok nastavuje politiku ako predvolenú povolenú v čase jej načítania.

gunicorn.te:

policy_module(gunicorn, 1.0)

type gunicorn_t;
type gunicorn_exec_t;
init_daemon_domain(gunicorn_t, gunicorn_exec_t)
permissive gunicorn_t;

Tento súbor politiky môžete skompilovať a pridať do svojho systému.

make -f /usr/share/selinux/devel/Makefile
sudo semodule -i gunicorn.pp

sudo semanage permissive -a gunicorn_t
sudo semodule -l | grep permissive

Pozrime sa, či SELinux neblokuje niečo iné, než k čomu pristupuje náš neznámy démon.

sudo ausearch -m AVC

type=AVC msg=audit(1545315977.237:1273): avc:  denied { write } for pid=19400 comm="nginx" name="gunicorn.sock" dev="tmpfs" ino=52977 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file permissive=0

SELinux bráni Nginxu zapisovať dáta do UNIX socketu, ktorý používa Gunicorn. V takýchto prípadoch sa zvyčajne politiky začnú meniť, ale pred nami sú ďalšie výzvy. Môžete tiež zmeniť nastavenia domény z domény obmedzení na doménu povolení. Teraz presuňme httpd_t do domény oprávnení. To poskytne Nginxu potrebný prístup a môžeme pokračovať v ďalšom ladení.

sudo semanage permissive -a httpd_t

Takže, keď sa vám podarí udržať SELinux chránený (naozaj by ste nemali nechať projekt SELinux v obmedzenom režime) a načítajú sa domény povolení, musíte zistiť, čo presne treba označiť ako gunicorn_exec_t, aby všetko fungovalo správne. znova. Skúsme navštíviť webovú stránku a zobraziť nové správy o obmedzeniach prístupu.

sudo ausearch -m AVC -c gunicorn

Uvidíte veľa správ obsahujúcich 'comm="gunicorn"', ktoré robia rôzne veci so súbormi v /srv/djangoapp, takže toto je zjavne jeden z príkazov, ktorý stojí za to označiť.

Okrem toho sa však zobrazí správa, ako je táto:

type=AVC msg=audit(1545320700.070:1542): avc:  denied { execute } for pid=20704 comm="(gunicorn)" name="python3.6" dev="vda3" ino=8515706 scontext=system_u:system_r:init_t:s0 tcontext=unconfined_u:object_r:var_t:s0 tclass=file permissive=0

Ak sa pozriete na stav služby gunicorn alebo spustíte príkaz ps, neuvidíte žiadne spustené procesy. Vyzerá to, že gunicorn sa pokúša získať prístup k interpreteru Pythonu v našom prostredí virtualenv, prípadne spustiť pracovné skripty. Takže teraz označme tieto dva spustiteľné súbory a uvidíme, či môžeme otvoriť našu testovaciu stránku Django.

chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6

Pred výberom novej značky bude potrebné reštartovať službu gunicorn. Môžete ho okamžite reštartovať alebo službu zastaviť a nechať ju spustiť soket, keď stránku otvoríte v prehliadači. Overte, či procesy dostali správne označenia pomocou ps.

ps -efZ | grep gunicorn

Nezabudnite neskôr vytvoriť normálnu politiku SELinux!

Ak sa teraz pozriete na správy AVC, posledná správa obsahuje permisívne=1 pre všetko, čo súvisí s aplikáciou, a permisívne=0 pre zvyšok systému. Ak pochopíte, aký druh prístupu potrebuje skutočná aplikácia, môžete rýchlo nájsť najlepší spôsob riešenia takýchto problémov. Ale dovtedy je najlepšie udržiavať systém v bezpečí a získať jasný a použiteľný audit projektu Django.

sudo ausearch -m AVC

Stalo!

Objavil sa fungujúci projekt Django s frontendom založeným na Nginx a Gunicorn WSGI. Nakonfigurovali sme Python 3 a PostgreSQL 10 z repozitárov RHEL 8 Beta. Teraz sa môžete posunúť vpred a vytvoriť (alebo jednoducho nasadiť) aplikácie Django alebo preskúmať ďalšie dostupné nástroje v RHEL 8 Beta na automatizáciu procesu konfigurácie, zlepšenie výkonu alebo dokonca kontajnerizáciu tejto konfigurácie.

Zdroj: hab.com

Pridať komentár