RHEL 8 Beta ponúka vývojárom množstvo nových možností, ktorých vymenovanie by mohlo zabrať celé stránky. Avšak, učiť sa nové veci je vždy lepšie prakticky, preto vám nižšie ponúkame praktikum reálneho vytvárania infraštruktúry aplikácií na báze Red Hat Enterprise Linux 8 Beta.

Za základ si vezmeme Python, medzi vývojármi obľúbený programovací jazyk, kombináciu Django a PostgreSQL, veľmi rozšírenú väzbu na tvorbu aplikácií, a nakonfigurujeme RHEL 8 Beta pre ich použitie. Potom pridáme ešte niekoľko (neverejných) ingrediencií.
Testovacie prostredie sa bude meniť, pretože sa chceme zoznámiť s možnosťami automatizácie, prácou s kontajnermi a vyskúšať prostredia s viacerými servermi. Pre začiatok práce na novom projekte môžeme začať vytvorením malého jednoduchého prototypu manuálne – týmto spôsobom môžeme vidieť, čo sa má presne stať a akým spôsobom prebieha interakcia, a potom prejsť k automatizácii a vytvoreniu zložitejších konfigurácií. Dnes sa budeme rozprávať o vytvorení takého prototypu.
Začneme s rozšírením obrazu virtuálneho stroja RHEL 8 Beta VM. Môžeme nainštalovať virtuálny stroj od nuly alebo použiť hostiteľský obraz KVM dostupný s predplatným na Beta. Pri použití hostiteľského obrazu bude potrebné nastaviť virtuálny CD, ktorý bude obsahovať metaúdaje a používateľské údaje pre cloudovú inicializáciu (cloud-init). Nebudete musieť robiť nič špeciálne so štruktúrou disku alebo dostupnými balíkmi, vyhovuje akákoľvek konfigurácia.
Poďme sa na celý proces pozrieť podrobnejšie.
Inštalácia Django
S najnovšou verziou Django bude potrebné virtuálne prostredie (virtualenv) s Python 3.5 alebo novšou verziou. V poznámkach k Beta môžeme vidieť, že je dostupný Python 3.6, poďme si skontrolovať, či je to naozaj tak:
[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found
Red Hat aktívne využíva Python ako systémový nástroj v RHEL, takže prečo dostávame takýto výsledok?
Ide o to, že mnohí vývojári používajúci Python stále premýšľajú o prechode z Python 2 na Python 2, pričom samotný Python 3 je v aktívnom vývoji a neustále sa objavujú nové verzie. Preto, aby sa uspokojila potreba po stabilných systémových nástrojoch a zároveň poskytol používateľom prístup k rôznym novým verziám Pythonu, systémový Python bol presunutý do nového balíka a zabezpečila sa možnosť inštalácie ako Python 2.7, tak aj 3.6. Podrobnejšie informácie o zmenách a dôvodoch, prečo k tomu došlo, nájdete v publikácii v (Langdon White).
Takže, na získanie funkčného Pythonu je potrebné nainštalovať len dva balíky, pričom python3-pip sa nainštaluje ako závislosť.
sudo yum install python36 python3-virtualenv
Prečo by ste nemali používať priame volania modulu, ako navrhuje Langdon, a nenainštalovať pip3? S ohľadom na pripravovanú automatizáciu je známe, že pre Ansible je potrebný nainštalovaný pip, keďže modul pip nepodporuje virtuálne prostredia (virtualenvs) s vlastným vykonateľným súborom pip.
Ak máte k dispozícii funkčný interpret python3, môžete pokračovať v procese inštalácie Django a získať funkčný systém spolu s ďalšími našimi komponentmi. V sieti je mnoho možností implementácie. Tu je jedna verzia, ale používateľ môže využiť vlastné procesy.
Verzie PostgreSQL a Nginx dostupné v RHEL 8 predvolene nainštalujeme pomocou Yum.
sudo yum install nginx postgresql-server
Pre PostgreSQL bude potrebný psycopg2, ale musí byť dostupný iba v prostredí virtualenv, preto ho nainštalujeme pomocou pip3 spolu s Django a Gunicorn. Ale najprv musíme nastaviť virtualenv.
O správnom výbere miesta inštalácie projektov Django sa vedie veľa diskusií, ale keď vzniknú nejaké pochybnosti, môžete sa vždy odvolať na štandard Linux Filesystem Hierarchy Standard. Konkrétne v FHS sa uvádza, že /srv sa používa na: "depozit dát špecifických pre konkrétny uzol, – údajov, ktoré systém generuje, ako sú údaje a skripty webových serverov, údaje uložené na FTP serveroch a tiež repozitáre systémov správy verzií (ktoré sa objavili v FHS-2.3 v roku 2004)."
Toto je presne náš prípad, a preto všetko potrebné ukladáme do /srv, ktorej vlastníkom je náš používateľ 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 nie je zložité: vytvoríme databázu, vytvoríme používateľa, nastavíme povolenia. Existuje jeden aspekt, na ktorý by ste mali pamätať pri počiatočnej inštalácii PostgreSQL – je to skript postgresql-setup, ktorý je dodávaný s balíkom postgresql-server. Tento skript pomáha vykonávať základné úlohy spojené s administráciou databázového klastrov, napríklad inicializáciu klastrov alebo proces aktualizácie. Pre nastavenie nového inštancie PostgreSQL na systéme RHEL musíme vykonať príkaz:
sudo /usr/bin/postgresql-setup -initdb
Po tomto môžeme spustiť PostgreSQL pomocou systemd, vytvoriť databázu a nastaviť projekt v Django. Nezabudnite reštartovať PostgreSQL po vykonaní zmien v konfiguračnom súbore na overovanie klienta (zvyčajne pg_hba.conf), aby ste nastavili uchovávanie hesla pre používateľa aplikácie. Ak sa stretnete s inými problémami, uistite sa, že boli zmenené 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ôžeme spustiť vývojový server, aby sme sa uistili, že všetko funguje. Po spustení vývojového servera je dobré vytvoriť používateľa admin na testovanie pripojenia k databáze.
./manage.py runserver 0.0.0.0:8000
./manage.py createsuperuser
WSGI? Čo to je?
Vývojový server je užitočný pri testovaní, ale na spustenie aplikácie je potrebné nakonfigurovať správny server a proxy pre Web Server Gateway Interface (WSGI). Existuje niekoľko bežných kombinácií, napríklad Apache HTTPD s uWSGI alebo Nginx s Gunicorn.
Úloha Web Server Gateway Interface je presmerovávať požiadavky od webového servera k webovému rámcu Python. WSGI je akýsi odkaz na hrozného minulosť, keď boli populárne mechanizmy CGI, a dnes je WSGI vlastne štandardom, nezávisle od toho, aký webový server alebo Python rámec používate. Napriek jeho širokej popularite však existuje množstvo nuáns pri práci s týmito rámcami a množstvo možností výberu. V tomto prípade sa pokúsime nastaviť spoluprácu medzi Gunicorn a Nginx cez socket.
Keďže sú oba tieto komponenty nainštalované na tom istom serveri, pokúsime sa namiesto sieťového socketu použiť socket UNIX. Keďže na komunikáciu v každom prípade potrebujeme socket, pokúsime sa urobiť ešte jeden krok a nastaviť aktiváciu socketu pre Gunicorn cez systemd.
Proces vytvárania služieb, aktivovaných socketmi (socket activated services), je dosť jednoduchý. Najprv sa vytvorí unit súbor, ktorý obsahuje direktívu ListenStream, určujúcu bod, kde bude vytvorený socket UNIX, potom unit súbor pre službu, v ktorom bude direktíva Requires odkazovať na unit súbor socketu. V zozname unit súboru služby zostáva len zavolať Gunicorn z virtuálneho prostredia a vytvoriť WSGI viazanosť pre socket UNIX a aplikáciu Django.
Tu je niekoľko príkladov unit súborov, ktoré môžete použiť ako základ. Najprv nastavíme socket.
[Unit]
Description=Gunicorn WSGI socket
[Socket]
ListenStream=\/run\/gunicorn.sock
[Install]
WantedBy=sockets.target
Teraz je potrebné nastaviť démon 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 stačí jednoducho vytvoriť súbory konfigurácie proxy a nastaviť adresár na ukladanie statického obsahu, ak ho používate. V RHEL sa konfiguračné súbory Nginx nachádzajú v \/etc\/nginx\/conf.d. Môžete tam skopírovať nasledujúci príklad do súboru \/etc\/nginx\/conf.d\/default.conf a spustiť službu. Uistite sa, že ste nastavili server_name v súlade s názvom vášho 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 socket Gunicorn a Nginx pomocou systemd, a môžete sa pustiť do testovania.
Chyba Bad Gateway?
Ak zadáte adresu do prehliadača, pravdepodobne dostanete chybu 502 Bad Gateway. Može to byť spôsobené nesprávne nastavenými povoleniami pre UNIX socket, alebo to môže byť spôsobené zložitejšími problémami súvisiacimi s riadením prístupu v SELinux.
V chybovom denníku nginx sa môžete stretnúť s podobným riadkom:
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 testujete Gunicorn priamo, obdržíte 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, pravdepodobne uvidíte, že problém súvisí so SELinux. Keďže máme spustený démon, pre ktorý nebola vytvorená vlastná politika, je označený ako init_t. Overíme túto teóriu v praxi.
sudo setenforce 0
Všetko toto môže spôsobiť kritiku a krvavé slzy, ale je to len ladenie prototypu. Vypneme kontrolu len kvôli tomu, aby sme sa uistili, že problém je skutočne v tomto, po čom všetko vrátime naspäť na svoje miesta.
Obnovením stránky v prehliadači alebo reštartovaním našej príkazu curl môžeme vidieť testovaciu stránku Django.
Takže, keď sme sa uistili, že všetko funguje a už nie sú žiadne problémy s povoleniami, znova zapneme SELinux.
sudo setenforce 1
Tu nebude reč o audit2allow a o vytváraní politík na základe upozornení pomocou sepolgen, pretože v súčasnosti neexistuje reálny Django aplikácia, a teda ani úplná mapa toho, k čomu by Gunicorn mohol chcieť mať prístup, a k čomu by sa tento prístup mal zakázať. Preto je potrebné zabezpečiť fungovanie SELinux na ochranu systému, a zároveň umožniť aplikácii spúšťať a zaznamenávať správy v audítorskom denníku, aby bolo možné na ich základe vytvoriť skutočnú politiku.
Určenie povolených domén (permissive domains)
O povolených doménach v SELinux nie sú všetci informovaní, ale nemajú nič nové. Mnohí s nimi dokonca pracovali, ani si toho neboli vedomí. Keď sa vytvára politika na základe správ auditu, vytvorená politika predstavuje povolenú doménu. Pokúsme sa vytvoriť najjednoduchšiu povoľovaciu politiku.
Na vytvorenie konkrétneho povoleného domény pre Gunicorn je potrebná určitá politika a bude potrebné 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 vynikajúci nástroj na odhaľovanie 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 politika povoleného domény pre Gunicorn čo najjednoduchšia – vyhlásime základný typ (gunicorn_t), vyhlásime typ, ktorý budeme používať na označenie niekoľkých spustiteľných súborov (gunicorn_exec_t) a potom nastavíme prechod (transition) pre system, aby sme správne označili spustené procesy. Posledný riadok nastavuje politiku ako povolenú predvolene 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 politík môžete skompilovať a pridať do 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
Skontrolujme, či SELinux neblokuje niečo iné, okrem toho, na čo sa odvoláva 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 nedovolí Nginxu zapisovať dáta do UNIX socketu používaného Gunicornom. Zvyčajne sa v takýchto prípadoch začnú meniť politiky, ale predtým treba riešiť aj ďalšie úlohy. Môžete tiež zmeniť nastavenia domény, previesť ju z domény obmedzení na doménu povolení. Teraz presunieme httpd_t do domény povolení. To zabezpečí Nginxu potrebný prístup a môžeme pokračovať v práci na ladení.
sudo semanage permissive -a httpd_t
Takže, keď sa podarilo zachovať ochranu SELinux (naozaj by sa nemal projekt opustiť s SELinuxom v režime obmedzení) a domény povolení sa načítavajú, je potrebné zistiť, čo presne treba označiť ako gunicorn_exec_t, aby všetko opäť fungovalo ako má. Pokúsme sa prístupovať na webovú stránku, aby sme videli nové správy o obmedzeniach v prístupe.
sudo ausearch -m AVC -c gunicorn
Môžeme vidieť množstvo správ, ktoré obsahujú 'comm="gunicorn"', a ktoré vykonávajú rôzne akcie na súboroch v /srv/djangoapp, takže je zrejmé, že ide o jednu z príkazov, ktoré stojí za to označiť.
Okrem toho sa zobrazuje správa tohto druhu:
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 vykonáte príkaz ps, nezobrazia sa žiadne spustené procesy. Vyzerá to, že gunicorn sa pokúša pristúpiť k interpretu Python vo našom prostredí virtualenv, pravdepodobne na spustenie pracovných skriptov (workers). Preto teraz označíme tieto dva vykonateľné súbory a skontrolujeme, či sa nám podarí otvoriť našu testovaciu stránku Django.
chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6
Bude potrebné reštartovať službu gunicorn, aby bolo možné vybrať novú značku. Môžete ju reštartovať okamžite alebo zastaviť službu a nechať socket, aby ju spustil pri otvorení stránky v prehliadači. Uistite sa, že procesy dostali potrebné značky, pomocou ps.
ps -efZ | grep gunicorn
Nezabudnite potom vytvoriť riadnu politiku SELinux!
Ak sa teraz pozriete na správy AVC, posledná správa obsahuje permissive=1 pre všetko, čo súvisí s aplikáciou, a permissive=0 pre celú ostatnú systém. Ak pochopíte, aký prístup je potrebný pre skutočnú aplikáciu, môžete rýchlejšie nájsť optimálny spôsob riešenia takýchto problémov. Ale dovtedy je lepšie, aby bol systém chránený, a aby ste získali zrozumiteľný a použiteľný audit projektu Django.
sudo ausearch -m AVC
Podarilo sa!
Máme funkčný projekt Django s frontendom na Nginx a Gunicorn WSGI. Nastavili sme Python 3 a PostgreSQL 10 z repozitárov RHEL 8 Beta. Teraz môžeme pokračovať v práci a vytvárať (alebo jednoducho nasadzovať) aplikácie Django alebo skú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: habr.com
