RHEL 8 Beta gyakorlati útmutató: Működő webalkalmazások összeállítása

A RHEL 8 Beta számos új lehetőséget kínál a fejlesztők számára, amelyeket sorolni is lehetne, de az új dolgok gyakorlati elsajátítása mindig a legjobb. Ezért alább egy valós alkalmazásinfrastruktúra létrehozásának gyakorlati útmutatóján haladhatunk végig, amely a Red Hat Enterprise Linux 8 Beta alapjait képezi.

RHEL 8 Beta gyakorlati útmutató: Működő webalkalmazások összeállítása

Alapként a Python-t választjuk, amely a fejlesztők körében népszerű programozási nyelv, valamint a Django és PostgreSQL kombinációját, ami egy széles körben használt csomópár az alkalmazások fejlesztésére. Ezt követően konfiguráljuk a RHEL 8 Beta-t, hogy működni tudjon ezzel a rendszerrel. Majd hozzáadunk még néhány (nem titkos) összetevőt.

A tesztkörnyezet folyamatosan változni fog, hiszen érdekli a lehetőségek felfedezése, a konténerkezelés és a több szerveres környezetek kipróbálása. Egy új projekt elindításához kezdhetjük a manuális, egyszerű prototípus létrehozásával – így láthatjuk, mi történik, valamint hogyan zajlik az egymásra épülés, majd ezt követően áttérhetünk az automatizálásra és bonyolultabb konfigurációk létrehozására. Ma erről a prototípus létrehozásáról lesz szó.

Kezdjük a RHEL 8 Beta VM virtuális gép képfájlának telepítésével. Telepíthetünk virtuális gépet a nulláról, vagy használhatjuk a KVM vendégképet, amely a Beta előfizetéssel érhető el. A vendégkép használata esetén be kell állítanunk egy virtuális CD-t, amely tartalmazza a cloud-init adatokat és felhasználói metainformációkat. Nincs szükség különleges változtatásokra a lemezkonstrukcióval vagy a rendelkezésre álló csomagokkal kapcsolatban, bármilyen konfiguráció megfelelő lesz.

Nézzük meg a teljes folyamatot részletesebben.

Django telepítése

A legújabb Django verzióhoz virtuális környezet (virtualenv) szükséges Python 3.5-ös vagy annál újabb verzióval. A Beta megjegyzései között láthatjuk, hogy Python 3.6 elérhető, ellenőrizzük, valóban így van-e:

[cloud-user@8beta1 ~]$ python
-bash: python: parancs nem található
[cloud-user@8beta1 ~]$ python3
-bash: python3: parancs nem található

A Red Hat aktívan használja a Python-t rendszerszerszként a RHEL-ben, miért van ilyen eredménye?

A tény az, hogy sok Python-fejlesztő még mindig fontolgatja a váltást a Python 2-ről a Python 2-re, miközben a Python 3 aktív fejlesztés alatt áll, és folyamatosan újabb és újabb verziók jelennek meg. Ezért, hogy megfeleljenek a stabil rendszereszközök iránti igénynek, és egyidejűleg hozzáférést biztosítsanak a felhasználóknak a különböző új Python verziókhoz, a rendszer Python-t egy új csomagba helyezték, lehetővé téve a Python 2.7 és 3.6 telepítését is. A változásokról és arról, miért történt ez, részletes információt találhatunk a Langdon White blogjában (Langdon White).

Tehát a működő Python megszerzéséhez mindössze két csomagot kell telepíteni, így a python3-pip mint függőség automatikusan be fog jönni.

sudo yum install python36 python3-virtualenv

Miért ne érdemes közvetlenül a modulra hivatkozni, ahogy Langdon javasolja, és miért ne telepítsük a pip3-at? Az automatizálásra való tekintettel tudvalévő, hogy az Ansible működéséhez szükséges a pip telepítése, mivel a pip modul nem támogatja a virtuális környezeteket (virtualenvs) egy testreszabott pip végrehajtható fájlal.

Ha rendelkezésre áll egy működő python3 értelmező, folytathatjuk a Django telepítési folyamatát, és működő rendszert kaphatunk más összetevőinkkel együtt. Az interneten számtalan megvalósítási lehetőség található. Itt egy verziót mutatunk be, de a felhasználók saját folyamataikat is alkalmazhatják.

A RHEL 8-ban alapértelmezetten elérhető PostgreSQL és Nginx verziókat Yum segítségével fogjuk telepíteni.

sudo yum install nginx postgresql-server

A PostgreSQL-hez szükség lesz a psycopg2-re, de ezt csak a virtualenv környezetben szeretnénk elérni, ezért pip3 segítségével telepítjük Django-val és Gunicorn-nal együtt. De először be kell állítanunk a virtualenv-t.

A Django projektek telepítési helyének helyes megválasztásáról mindig sok vita folyik, de ha kétségek merülnek fel, mindig fordulhatunk a Linux Filesystem Hierarchy Standardhoz. Különösen a FHS azt mondja, hogy /srv-t arra használják, hogy: „adatokat tároljanak, amelyek egy adott csomópont specifikusak – adatokat, amelyeket a rendszer kiad, például a webkiszolgálók adatait és scriptjeit, FTP-szervereken tárolt adatokat, valamint verziókezelő rendszerek adattárait (amelyek a FHS-2.3-ban jelentek meg 2004-ben)”.

Ez pontosan a mi esetünk, ezért mindent összegyűjtünk a /srv mappába, amelynek a tulajdonosa az alkalmazás felhasználója (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

A PostgreSQL és Django beállítás nem okoz nehézséget: adatbázist hozunk létre, felhasználót hozunk létre, és beállítjuk az engedélyeket. Van egy dolog, amire érdemes figyelni a PostgreSQL alaptelepítésekor – ez a postgresql-setup szkript, amely a postgresql-server csomaggal együtt kerül telepítésre. Ez a szkript segít az adatbázis klaszter adminisztrációval kapcsolatos alapvető feladatok elvégzésében, például a klaszter inicializálásában vagy a frissítési folyamatban. Egy új PostgreSQL példány beállításához a RHEL rendszeren a következő parancsot kell futtatni:

sudo /usr/bin/postgresql-setup --initdb

Ezt követően elindíthatjuk a PostgreSQL-t a systemd segítségével, létrehozhatunk adatbázist, és beállíthatjuk a projektet Django-ban. Ne felejtsük el újraindítani a PostgreSQL-t, miután módosítottuk az ügyfél hitelesítési konfigurációs fájlját (általában ez a pg_hba.conf), hogy beállítsuk az alkalmazás felhasználó jelszavának tárolását. Ha más nehézségekkel találkozunk, győződjünk meg róla, hogy módosítottuk az IPv4 és IPv6 beállításokat a pg_hba.conf fájlban.

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

A /var/lib/pgsql/data/pg_hba.conf fájlban:

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

A /srv/djangoapp/settings.py fájlban:

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

A settings.py fájl és az adatbázis konfigurálása után elindíthatjuk a fejlesztői szervert, hogy megbizonyosodjunk arról, hogy minden működik. A fejlesztői szerver elindítása után nem árt létrehozni egy admin felhasználót az adatbázishoz való csatlakozás teszteléséhez.

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

WSGI? Mi az?

A fejlesztői szerver hasznos a tesztelés során, de az alkalmazás futtatásához megfelelő szervert és proxyt kell beállítani a Web Server Gateway Interface (WSGI) számára. Számos elterjedt párosítás létezik, például Apache HTTPD uWSGI-vel vagy Nginx Gunicorn-nal.

A Web Server Gateway Interface feladata a kérések továbbítása a webszerver a Python web framework. WSGI is a remnant of a terrible past when CGI mechanisms were in use, and today WSGI is essentially the standard, regardless of the web server or Python framework used. However, despite its widespread use, there are still many nuances when working with these frameworks and a multitude of options available. In this case, we'll try to establish interaction between Gunicorn and Nginx via a socket.

Since both of these components are installed on the same server, we'll attempt to use a UNIX socket instead of a network socket. As a socket is needed for communication anyway, let's take another step and configure the socket activation for Gunicorn via systemd.

The process of creating socket-activated services is fairly straightforward. First, a unit file is created that contains the ListenStream directive, specifying the point where the UNIX socket will be created. Then, a unit file for the service is created, where the Requires directive will point to the socket unit file. Finally, in the service unit file, it remains only to call Gunicorn from the virtual environment and create a WSGI binding for the UNIX socket and the Django application.

Here are some example unit files that you can use as a basis. First, let's configure the socket.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Now it's time to configure the Gunicorn daemon.

[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

For Nginx, it is sufficient to create proxy configuration files and set up the directory for storing static content if you are using one. In RHEL, Nginx configuration files are located in /etc/nginx/conf.d. You can copy the following example into the file /etc/nginx/conf.d/default.conf and start the service. Ensure that you specify server_name according to your host name.

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

Start the Gunicorn and Nginx sockets using systemd, and you can begin testing.

Bad Gateway hiba?

Ha beírja az URL címet a böngészőbe, valószínűleg 502 Bad Gateway hibát fog kapni. Ezt a UNIX socketekhez tartozó jogosultságok helytelen beállítása okozhatja, vagy bonyolultabb problémák is állhatnak a háttérben a SELinux hozzáférés-kezelésével kapcsolatban.

Az nginx hibák között ilyesmi összefoglalót találhatunk:

2018/12/18 15:38:03 [kritikus] 12734#0: *3 connect() to unix:/run/gunicorn.sock failed (13: Engedély megtagadva) 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"

Ha a Gunicorn-t közvetlenül teszteljük, üres választ kapunk.

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

Nézzük meg, miért történik ez. Ha megnyitjuk a naplót, valószínűleg láthatjuk, hogy a probléma a SELinux-szal kapcsolatos. Mivel fut egy démon, amelyhez nem készült saját politika, init_t-ként van megjelölve. Ellenőrizzük ezt a gyakorlatban.

sudo setenforce 0

Mindez kritikát és keserves könnyeket okozhat, de ez csupán a prototípus hibakeresése. Kikapcsoljuk a ellenőrzést, hogy megbizonyosodjunk arról, hogy a probléma itt rejlik, majd visszaállítunk mindent a helyére.

Frissítve a böngészőben az oldalt, vagy újraindítva a curl parancsunkat, láthatjuk a Django tesztoldalt.

Tehát, miután megbizonyosodtunk róla, hogy minden működik, és többé nincs jogosultsági probléma, újból bekapcsoljuk a SELinux-t.

sudo setenforce 1

Itt nem lesz szó az audit2allow-ról és a politikák létrehozásáról az értesítések alapján a sepolgen segítségével, mivel jelenleg nincs valós Django alkalmazás, ezért nincs teljes térkép arról, hogy mire szeretne nogunicorn hozzáférni, és hogy mit kellene megtiltani. Ezért fontos, hogy a SELinux védje a rendszert, és ugyanakkor engedje meg az alkalmazásnak, hogy fusson és naplózza a tevékenységeket, hogy később ki tudjunk dolgozni egy valódi politikát.

Engedélyezett domainek (permissive domains)

A SELinux engedélyezett domainekkel nem mindenki találkozott, de nem új dolog ez. Sokan dolgoztak már velük, anélkül hogy tudták volna. Amikor egy politika készül audit üzenetek alapján, a létrehozott politika mindig egy engedélyezett domainek. Próbáljunk meg létrehozni egy egyszerű engedélyező politikát.

A Gunicorn konfigurált engedélyezett domain létrehozásához szükség van egy bizonyos politikára, valamint meg kell jelölni a megfelelő fájlokat. Ezenkívül szükségesek eszközök az új politikák összeállításához.

sudo yum install selinux-policy-devel

Az engedélyezett domainek mechanizmusa nagyszerű eszköz a problémák azonosítására, különösen egyedi alkalmazások vagy olyan alkalmazások esetén, amelyek már meglévő politikák nélkül kerülnek forgalomba. Ebben az esetben a Gunicorn engedélyezett domain politikája a lehető legegyszerűbb lesz – deklaráljuk az alaptípust (gunicorn_t), meghatározzuk a típust, amelyet több végrehajtási fájl megjelölésére használunk (gunicorn_exec_t), majd beállítjuk az átmenetet (transition) a system számára a futó folyamatok helyes megjelöléséhez. Az utolsó sor az alapértelmezett politikát engedélyezettként állítja be az indításakor.

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;

Ezt a politikai fájlt le lehet fordítani és hozzá lehet adni a rendszerhez.

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

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

Nézzük meg, hogy a SELinux blokkol-e még valamit, azon kívül, amit a mi ismeretlen démonunk kér.

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

A SELinux nem engedi, hogy az Nginx írjon a Gunicorn által használt UNIX socketbe. Ilyen esetekben általában elkezdik módosítani a politikákat, de több feladat is vár ránk. A domain beállításait is meg lehet változtatni, átmenetileg engedélyezett doménné alakítva. Most áthelyezzük az httpd_t-t az engedélyezett doménbe. Ez biztosítja, hogy az Nginx hozzáférést kapjon, és folytathatjuk a hibaelhárítást.

sudo semanage permissive -a httpd_t

Tehát, miután sikerült megőrizni a SELinux védelmét (valójában nem szabad egy projektet SELinux korlátozó üzemmódban hagyni), és az engedélyezett domainek betöltődtek, meg kell határozni, hogy mit kell gunicorn_exec_t-ként megjelölni, hogy minden újra megfelelően működjön. Próbáljunk hozzáférni a weboldalhoz, hogy lássuk az új hozzáférési korlátozásokról szóló üzeneteket.

sudo ausearch -m AVC -c gunicorn

Látható, hogy számos üzenet található, melyekben a 'comm="gunicorn"' szerepel, amelyek különböző műveleteket hajtanak végre a /srv/djangoapp fájlokon, így nyilvánvaló, hogy ez éppen egy olyan parancs, amelyet érdemes megjelölni.

De ezen kívül megjelenik egy ilyen üzenet:

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

Ha megnézzük a gunicorn szolgáltatás állapotát, vagy végrehajtjuk a ps parancsot, nem fogunk látni futó folyamatokat. Úgy tűnik, hogy a gunicorn próbálja elérni a Python értelmezőt a virtualenv környezetünkben, valószínűleg a munkafolyamatok (workers) indításához. Ezért most jelöljük meg ezeket a két végrehajtható fájlt, és nézzük meg, sikerül-e megnyitni a teszt Django oldalunkat.

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

A gunicorn szolgáltatást újra kell indítani, hogy elérje az új címkét. Ezt azonnal újra indíthatja, vagy leállíthatja a szolgáltatást, és hagyhatja, hogy a socket indítsa el, amikor a weboldalt a böngészőben megnyitja. Győződjön meg róla, hogy a folyamatok megkapták a szükséges címkéket, a ps használatával.

ps -efZ | grep gunicorn

Ne felejtsen el később egy normális SELinux politikát létrehozni!

Ha most megnézzük az AVC üzeneteket, az utolsó üzenet permissive=1-t tartalmaz mindarra, ami az alkalmazásra vonatkozik, és permissive=0-t az egész rendszer többi részére. Ha megértjük, hogy a valós alkalmazásnak pontosan milyen hozzáférésre van szüksége, gyorsabban találhatunk optimalizált megoldást az ilyen problémákra. De addig a legjobb, ha a rendszer védett, és ha világos és használható auditot kapunk a Django projektről.

sudo ausearch -m AVC

Sikerült!

Megjelent egy működő Django projekt Nginx és Gunicorn WSGI frontenddel. Beállítottuk a Python 3-at és a PostgreSQL 10-et a RHEL 8 Beta repozitóriumaiból. Most továbbléphetünk és létrehozhatunk (vagy egyszerűen telepíthetünk) Django alkalmazásokat, vagy felfedezhetjük a RHEL 8 Beta más elérhető eszközeit a beállítási folyamat automatizálására, teljesítményjavításra vagy akár a konfiguráció konténerbe helyezésére.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster