Workshop RHEL 8 Beta: Vytváření funkčních webových aplikací

RHEL 8 Beta nabízí vývojářům mnoho nových funkcí, jejichž výpis by mohl zabrat stránky, nicméně učit se novým věcem je v praxi vždy lepší, proto níže nabízíme workshop o skutečném vytvoření aplikační infrastruktury založené na Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Vytváření funkčních webových aplikací

Vezměme si jako základ Python, oblíbený programovací jazyk mezi vývojáři, kombinaci Django a PostgreSQL, celkem běžnou kombinaci pro tvorbu aplikací, a nakonfigurujme RHEL 8 Beta pro práci s nimi. Pak přidáme ještě pár (nezařazených) ingrediencí.

Testovací prostředí se změní, protože je zajímavé prozkoumat možnosti automatizace, práce s kontejnery a vyzkoušet prostředí s více servery. Chcete-li začít s novým projektem, můžete začít ručním vytvořením malého, jednoduchého prototypu, abyste přesně viděli, co se musí stát a jak to spolupůsobí, a poté přejít k automatizaci a vytváření složitějších konfigurací. Dnes se bavíme o vytvoření takového prototypu.

Začněme nasazením obrazu virtuálního počítače RHEL 8 Beta. Virtuální počítač můžete nainstalovat úplně od začátku nebo použít hostující obraz KVM dostupný s vaším předplatným Beta. Při použití bitové kopie hosta budete muset nakonfigurovat virtuální CD, které bude obsahovat metadata a uživatelská data pro cloudovou inicializaci (cloud-init). Se strukturou disku nebo dostupnými balíčky nemusíte dělat nic zvláštního, stačí jakákoli konfigurace.

Pojďme se na celý proces podívat blíže.

Instalace Django

S nejnovější verzí Django budete potřebovat virtuální prostředí (virtualenv) s Pythonem 3.5 nebo novějším. V poznámkách k beta verzi můžete vidět, že Python 3.6 je k dispozici, pojďme zkontrolovat, zda je tomu skutečně tak:

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

Red Hat aktivně používá Python jako systémovou sadu nástrojů v RHEL, tak proč k tomu dochází?

Faktem je, že mnoho vývojářů Pythonu stále uvažuje o přechodu z Pythonu 2 na Python 2, zatímco samotný Python 3 je v aktivním vývoji a neustále se objevují další a další nové verze. Abychom vyhověli potřebě stabilních systémových nástrojů a zároveň nabídli uživatelům přístup k různým novým verzím Pythonu, byl systémový Python přesunut do nového balíčku a poskytl možnost nainstalovat Python 2.7 i 3.6. Více informací o změnách a o tom, proč byly provedeny, najdete v publikaci v Blog Langdona Whitea (Langdon White).

Takže pro zprovoznění Pythonu stačí nainstalovat dva balíčky, přičemž jako závislost je zahrnut python3-pip.

sudo yum install python36 python3-virtualenv

Proč nepoužít přímé volání modulů, jak navrhuje Langdon, a nenainstalovat pip3? S ohledem na nadcházející automatizaci je známo, že Ansible bude ke spuštění vyžadovat nainstalovaný pip, protože modul pip nepodporuje virtualenvs s vlastním spustitelným souborem pip.

S funkčním interpretem python3, který máte k dispozici, můžete pokračovat v instalačním procesu Django a mít funkční systém spolu s našimi dalšími komponentami. Na internetu je k dispozici mnoho možností implementace. Je zde uvedena jedna verze, ale uživatelé mohou používat své vlastní procesy.

Verze PostgreSQL a Nginx dostupné v RHEL 8 nainstalujeme standardně pomocí Yum.

sudo yum install nginx postgresql-server

PostgreSQL bude vyžadovat psycopg2, ale musí být dostupný pouze v prostředí virtualenv, takže jej nainstalujeme pomocí pip3 spolu s Django a Gunicornem. Nejprve ale musíme nastavit virtualenv.

Na téma výběru správného místa pro instalaci projektů Django se vždy vede spousta debat, ale v případě pochybností se vždy můžete obrátit na Linux Filesystem Hierarchy Standard. Konkrétně FHS říká, že /srv se používá k: „ukládání dat specifických pro hostitele – dat, která systém produkuje, jako jsou data a skripty webového serveru, data uložená na FTP serverech a řízení úložišť systému.“ verze (objevující se ve FHS -2.3 v roce 2004).

To je přesně náš případ, takže vše potřebné vložíme do /srv, který vlastní uživatel naší aplikace (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

Nastavení PostgreSQL a Django je snadné: vytvořte databázi, vytvořte uživatele, nakonfigurujte oprávnění. Jedna věc, kterou je třeba mít na paměti při počáteční instalaci PostgreSQL, je skript postgresql-setup, který se instaluje s balíčkem postgresql-server. Tento skript vám pomáhá provádět základní úlohy spojené s administrací klastru databází, jako je inicializace klastru nebo proces upgradu. Pro konfiguraci nové instance PostgreSQL v systému RHEL musíme spustit příkaz:

sudo /usr/bin/postgresql-setup -initdb

Poté můžete spustit PostgreSQL pomocí systemd, vytvořit databázi a nastavit projekt v Django. Nezapomeňte restartovat PostgreSQL po provedení změn v konfiguračním souboru autentizace klienta (obvykle pg_hba.conf), abyste nakonfigurovali úložiště hesel pro uživatele aplikace. Pokud narazíte na jiné potíže, nezapomeňte změnit nastavení IPv4 a IPv6 v souboru 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 souboru /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 souboru /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 konfiguraci souboru settings.py v projektu a nastavení konfigurace databáze můžete spustit vývojový server, abyste se ujistili, že vše funguje. Po spuštění vývojového serveru je vhodné vytvořit administrátora, aby bylo možné otestovat připojení k databázi.

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

WSGI? Wai?

Vývojový server je užitečný pro testování, ale ke spuštění aplikace musíte nakonfigurovat příslušný server a proxy pro rozhraní webového serveru (WSGI). Existuje několik běžných kombinací, například Apache HTTPD s uWSGI nebo Nginx s Gunicorn.

Úkolem rozhraní Web Server Gateway Interface je předávat požadavky z webového serveru do webového rámce Pythonu. WSGI je pozůstatkem strašlivé minulosti, kdy existovaly CGI motory, a dnes je WSGI de facto standardem bez ohledu na použitý webový server nebo framework Python. Ale i přes jeho rozšířené používání existuje při práci s těmito frameworky stále mnoho nuancí a mnoho možností. V tomto případě se pokusíme navázat interakci mezi Gunicornem a Nginxem prostřednictvím zásuvky.

Protože jsou obě tyto komponenty nainstalovány na stejném serveru, zkusme místo síťového soketu použít UNIXový soket. Protože komunikace v každém případě vyžaduje socket, zkusme udělat ještě jeden krok a nakonfigurovat aktivaci socketu pro Gunicorn přes systemd.

Proces vytváření soket aktivovaných služeb je poměrně jednoduchý. Nejprve se vytvoří soubor jednotek, který obsahuje direktivu ListenStream ukazující na bod, ve kterém bude vytvořen soket UNIX, potom soubor jednotek pro službu, ve které bude direktiva Requires ukazovat na soubor jednotek soketu. Poté v souboru servisní jednotky zbývá pouze zavolat Gunicorn z virtuálního prostředí a vytvořit vazbu WSGI pro UNIX socket a aplikaci Django.

Zde je několik příkladů souborů jednotek, které můžete použít jako základ. Nejprve nastavíme zásuvku.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Nyní musíte nakonfigurovat 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

Pro Nginx je to jednoduchá záležitost vytvoření konfiguračních souborů proxy a nastavení adresáře pro ukládání statického obsahu, pokud jej používáte. V RHEL jsou konfigurační soubory Nginx umístěny v /etc/nginx/conf.d. Následující příklad můžete zkopírovat do souboru /etc/nginx/conf.d/default.conf a spustit službu. Ujistěte se, že jste nastavili název_serveru tak, aby odpovídal vašemu názvu hostitele.

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;
   }
}

Spusťte soket Gunicorn a Nginx pomocí systemd a jste připraveni začít testovat.

Chyba špatné brány?

Pokud zadáte adresu do prohlížeče, pravděpodobně se vám zobrazí chyba 502 Bad Gateway. Může to být způsobeno nesprávně nakonfigurovanými oprávněními pro UNIX socket nebo to může být způsobeno složitějšími problémy souvisejícími s řízením přístupu v SELinuxu.

V protokolu chyb nginx můžete vidět řádek jako tento:

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"

Pokud otestujeme přímo Gunicorn, dostaneme prázdnou odpověď.

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

Pojďme zjistit, proč se to děje. Pokud otevřete protokol, s největší pravděpodobností uvidíte, že problém souvisí se SELinuxem. Protože provozujeme démona, pro kterého nebyla vytvořena žádná politika, je označen jako init_t. Pojďme si tuto teorii ověřit v praxi.

sudo setenforce 0

To vše může způsobit kritiku a slzy krve, ale to je jen ladění prototypu. Zakažme kontrolu, abychom se ujistili, že se jedná o problém, po kterém vše vrátíme na své místo.

Obnovením stránky v prohlížeči nebo opětovným spuštěním našeho příkazu curl můžete zobrazit testovací stránku Django.

Takže když jsme se ujistili, že vše funguje a již nejsou žádné problémy s oprávněními, znovu povolíme SELinux.

sudo setenforce 1

Nebudu zde mluvit o audit2allow nebo vytváření zásad založených na výstrahách pomocí sepolgenu, protože v tuto chvíli neexistuje žádná skutečná aplikace Django, takže neexistuje úplná mapa toho, k čemu by Gunicorn mohl chtít přistupovat a k čemu by měl přístup odepřít. Proto je nutné udržovat SELinux v chodu, aby byl systém chráněn, a zároveň umožnit běh aplikace a zanechat zprávy v audit logu, aby z nich pak mohla být vytvořena skutečná politika.

Určení permisivních domén

Ne každý slyšel o povolených doménách v SELinuxu, ale nejsou žádnou novinkou. Mnozí s nimi dokonce pracovali, aniž by si to uvědomovali. Když je zásada vytvořena na základě auditních zpráv, vytvořená zásada představuje vyřešenou doménu. Zkusme vytvořit jednoduchou povolovací politiku.

Chcete-li vytvořit konkrétní povolenou doménu pro Gunicorn, potřebujete nějakou politiku a také musíte označit příslušné soubory. Kromě toho jsou potřebné nástroje k sestavování nových politik.

sudo yum install selinux-policy-devel

Mechanismus povolených domén je skvělým nástrojem pro identifikaci problémů, zejména pokud jde o vlastní aplikaci nebo aplikace, které se dodávají bez již vytvořených zásad. V tomto případě bude povolená doménová politika pro Gunicorn co nejjednodušší – deklarujte hlavní typ (gunicorn_t), deklarujte typ, který použijeme k označení více spustitelných souborů (gunicorn_exec_t), a poté nastavte přechod, aby systém správně označil běžící procesy. Poslední řádek nastavuje zásadu jako výchozí povolenou v době načítání.

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 soubor zásad můžete zkompilovat a přidat do svého 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

Pojďme zkontrolovat, zda SELinux neblokuje něco jiného, ​​než k čemu přistupuje náš neznámý 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 zabraňuje Nginxu zapisovat data do UNIXového socketu používaného Gunicornem. Obvykle se v takových případech začnou politiky měnit, ale čekají nás další výzvy. Můžete také změnit nastavení domény z domény omezení na doménu oprávnění. Nyní přesuneme httpd_t do domény oprávnění. To poskytne Nginxu potřebný přístup a můžeme pokračovat v dalším ladění.

sudo semanage permissive -a httpd_t

Jakmile se vám tedy podaří udržet SELinux chráněný (opravdu byste neměli nechat projekt SELinux v omezeném režimu) a načteny domény oprávnění, musíte zjistit, co přesně je třeba označit jako gunicorn_exec_t, aby vše fungovalo správně znovu. Zkusme navštívit web a zobrazit nové zprávy o omezení přístupu.

sudo ausearch -m AVC -c gunicorn

Uvidíte spoustu zpráv obsahujících 'comm="gunicorn"', které dělají různé věci se soubory v /srv/djangoapp, takže toto je zjevně jeden z příkazů, který stojí za to označit.

Ale kromě toho se objeví zpráva jako tato:

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

Pokud se podíváte na stav služby gunicorn nebo spustíte příkaz ps, neuvidíte žádné běžící procesy. Vypadá to, že se gunicorn pokouší získat přístup k interpretru Pythonu v našem prostředí virtualenv, případně spustit pracovní skripty. Nyní tedy označíme tyto dva spustitelné soubory a zkontrolujeme, zda můžeme otevřít naši testovací stránku Django.

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

Než bude možné vybrat nový štítek, bude nutné restartovat službu gunicorn. Můžete jej okamžitě restartovat nebo službu zastavit a nechat ji spustit socket, když stránku otevřete v prohlížeči. Ověřte, že procesy obdržely správné štítky pomocí ps.

ps -efZ | grep gunicorn

Nezapomeňte později vytvořit normální politiku SELinux!

Pokud se nyní podíváte na zprávy AVC, poslední zpráva obsahuje permisivní=1 pro vše, co souvisí s aplikací, a permisivní=0 pro zbytek systému. Pokud rozumíte tomu, jaký druh přístupu skutečná aplikace potřebuje, můžete rychle najít nejlepší způsob, jak takové problémy vyřešit. Ale do té doby je nejlepší udržovat systém v bezpečí a získat jasný a použitelný audit projektu Django.

sudo ausearch -m AVC

Stalo!

Objevil se fungující projekt Django s frontendem založeným na Nginx a Gunicorn WSGI. Nakonfigurovali jsme Python 3 a PostgreSQL 10 z repozitářů RHEL 8 Beta. Nyní se můžete posunout vpřed a vytvořit (nebo jednoduše nasadit) aplikace Django nebo prozkoumat další dostupné nástroje v RHEL 8 Beta pro automatizaci procesu konfigurace, zlepšení výkonu nebo dokonce kontejnerizaci této konfigurace.

Zdroj: www.habr.com

Přidat komentář