Praktikë RHEL 8 Beta: Ndërtojmë aplikacione web që funksionojnë

RHEL 8 Beta ofron shumë mundësi të reja për zhvilluesit, të cilat mund të zënë faqe të gjithë, megjithatë, është më mirë të studiohet e reja në praktikë, prandaj më poshtë propozojmë të kaloni në një praktikë të krijimit të infrastrukturës së aplikacioneve në bazë të Red Hat Enterprise Linux 8 Beta.

Praktikë RHEL 8 Beta: Ndërtojmë aplikacione web që funksionojnë

Do të marrim si bazë Python, një gjuhë programimi e njohur mes zhvilluesve, kombinimin Django dhe PostgreSQL, një lidhje mjaft e zakonshme për krijimin e aplikacioneve dhe do të konfigurojmë RHEL 8 Beta për të punuar me to. Më pas do të shtojmë disa (jo sekrete) përbërës të tjerë.

Mjedisi testues do të ndryshojë, pasi është intriguese të studiohen mundësitë e automatizmit, puna me konteinerët dhe të provohet ambienti me disa serverë. Për të filluar punën me projektin e ri, mund të fillohet me krijimin e një prototipi të vogël manualisht - kështu mund të shihet se çfarë duhet të ndodhë dhe si realizohet ndërveprimi, e më pas të kaloni në automatizim dhe krijim të konfigurimeve më të ndërlikuara. Sot do të flasim për krijimin e një prototipi të tillë.

Të fillojmë me shpërndarjen e imazhit të makinës virtuale RHEL 8 Beta VM. Mund të instaloni makinën virtuale nga zero, ose të përdorni imazhin mysafir KVM, i cili është në dispozicion me abonimin për Beta. Kur përdorni imazhin mysafir, do të duhet të konfiguroni një CD virtual, i cili do të përmbajë metadatat dhe të dhënat e përdoruesit për inicializimin në cloud (cloud-init). Nuk ka nevojë të bëni ndonjë gjë të veçantë me strukturën e diskut ose paketat e disponueshme, çdo konfigurim do të ishte i përshtatshëm.

Le të shqyrtojmë të gjithë procesin më në detaje.

Instalimi i Django

Me versionin më të ri të Django do të nevojitet një ambient virtual (virtualenv) me Python 3.5 ose një version më të ri. Në shënimet për Beta mund të shihni se Python 3.6 është i disponueshëm, le të kontrollojmë nëse është vërtet kështu:

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

Red Hat e përdor aktivisht Python si mjet sistemor në RHEL, kështu që pse po merrni një rezultat të tillë?

Shkaku është se shumë zhvillues që përdorin Python, ende po mendojnë për kalimin nga Python 2 në Python 2, ndërkohë që vetë Python 3 ndodhet në fazën e zhvillimit aktiv, dhe vazhdimisht po dalin versione të reja. Prandaj, për të përmbushur nevojën për mjete stabilë sistemesh, dhe njëherazi për t'u ofruar përdoruesve akses në versione të ndryshme të reja të Python, Python-i sistemor u transferua në një paketë të re dhe u sigurua mundësia e instalimit të Python 2.7 dhe 3.6. Më shumë informacion mbi ndryshimet dhe pse kjo u bë, mund të gjendet në publikimin në blogun e Langdon White (Langdon White).

Pra, për të marrë një Python funksional, nevojiten vetëm dy paketa, ndërsa python3-pip do të tërhiqet si një varësi.

sudo yum install python36 python3-virtualenv

Pse nuk duhet të përdorim qasjet e drejtpërdrejta në modul, siç sugjeron Langdon, dhe të instalojmë pip3? Duke mbajtur parasysh automatizimin e ardhshëm, këtu është e njohur që për të punuar me Ansible kërkohet pip i instaluar, pasi moduli pip nuk mbështet mjediset virtuale (virtualenvs) me një skedar ekzekutiv pip të personalizuar.

Duke pasur një interpretues funksional python3, mund të vazhdojmë me procesin e instalimit të Django dhe të marrim një sistem funksional së bashku me komponentët tanë të tjerë. Në internet janë shumë variante implementimi. Këtu është një version, por përdoruesit mund të përdorin proceset e tyre.

Versionet e PostgreSQL dhe Nginx, të disponueshëm në RHEL 8 për default, do të installs me Yum.

sudo yum install nginx postgresql-server

Për PostgreSQL do të kërkohet psycopg2, por duhet që kjo të jetë e disponueshme vetëm në mjedisin virtualenv, prandaj do ta instalojmë me pip3 së bashku me Django dhe Gunicorn. Por së pari, na nevojitet të konfigurojmë virtualenv.

Në lidhje me përzgjedhjen e duhur të vendit të instalimit për projektet Django, gjithmonë ka shumë polemika, por kur lindin dyshime, gjithmonë mund të referohemi në standardin Linux Filesystem Hierarchy Standard. Në veçanti, në FHS thuhet se /srv përdoret për: "ruajtjen e të dhënave specifike për një nyjë të caktuar - të dhëna që siguron sistemi, për shembull, të dhëna dhe skripte nga serverët e web-it, të dhëna që ruhen në serverat FTP, si dhe repositorët e sistemeve të kontrollit të versioneve (të shfaqur në FHS-2.3 në vitin 2004)".

Ky është rast idh, prandaj ne grumbullojmë gjithçka të nevojshme në /srv, pronari i së cilës është përdoruesi ynë i aplikacionit (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

Konfigurimi i PostgreSQL dhe Django nuk shkakton ndonjë vështirësi: krijojmë bazën e të dhënave, krijojmë përdoruesin, konfigurimi i lejeve. Ka një pikë që duhet mbajtur mend gjatë instalimit origjinal të PostgreSQL - ky është skripti postgresql-setup, i cili instalohet së bashku me paketën postgresql-server. Ky skript ndihmon në kryerjen e detyrave bazë që lidhen me administrimin e marrëveshjes së të dhënave, për shembull, inicializimin e marrëveshjes ose procesin e përditësimit. Për të konfiguruar një instancë të re të PostgreSQL në sistemin RHEL, na nevojitet të ekzekutojmë komandën:

sudo /usr/bin/postgresql-setup -initdb

Pas kësaj, mund të nisni PostgreSQL përdorur systemd, të krijoni bazën e të dhënave dhe të konfiguroni projektin në Django. Mos harroni të rinisni PostgreSQL pas ndryshimeve në skedarin e konfigurimit të autentikimit të klientit (zakonisht pg_hba.conf) për të konfiguruar ruajtjen e fjalëkalimit për përdoruesin e aplikacionit. Nëse hasni ndonjë vështirësi tjetër, sigurohuni që të keni ndryshuar konfigurimet IPv4 dhe IPv6 në skedarin 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

NĂ« skedarin /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

NĂ« skedarin /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 }}',
   }
}

Pas konfigurimit të skedarit settings.py në projekt dhe konfigurimit të bazës së të dhënave, mund të nisni serverin e zhvillimit për të siguruar që gjithçka funksionon. Pas nisjes së serverit të zhvillimit, është mirë të krijoni një përdorues admin për të testuar lidhjen me bazën e të dhënave.

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

WSGI? ÇfarĂ«?

Serveri i zhvillimit është i dobishëm për testim, por për të nisur aplikacionin duhet të konfigurohet serveri dhe proxy i duhur për Web Server Gateway Interface (WSGI). Ka disa kombinime të zakonshme, për shembull, Apache HTTPD me uWSGI ose Nginx me Gunicorn.

Detyra e Web Server Gateway Interface është të ridrejtojë kërkesat nga serverin web në framework-un e Python. WSGI është një trashëgimi e keqe e së kaluarës, kur ishin në përdorim mekanizmat CGI, dhe sot WSGI është në fakt standardi, pavarësisht nga serveri web ose framework-u i Python që përdoret. Por përkundër përhapjes së tij të gjerë, ende ekzistojnë shumë nuanca kur punohet me këto framework-e, dhe shumë mundësi zgjedhjeje. Në këtë rast do të përpiqemi të krijojmë një ndërveprim midis Gunicorn dhe Nginx përmes një socket-i.

Duke qenë se këto dy komponentë janë instaluar në të njëjtin server, do të provojmë të përdorim një socket UNIX në vend të një socket-i rrjet. Pasi që në çdo rast, për komunikimin ne kemi nevojë për një socket, do të provojmë të bëjmë edhe një hap tjetër dhe të konfigurojmë aktivizimin e socket-it për Gunicorn nëpërmjet systemd.

Procesi i krijimit tĂ« shĂ«rbimeve qĂ« aktivizohen nga socket-at (shĂ«rbime tĂ« aktivizuara nga socket) Ă«shtĂ« mjaft i thjeshtĂ«. SĂ« pari krijohet njĂ« skedĂ« unit, e cila pĂ«rmban direktivĂ«n ListenStream, qĂ« tregon pikĂ«n ku do tĂ« krijohet socket-i UNIX; mĂ« pas – skedĂ« unit pĂ«r shĂ«rbimin, nĂ« tĂ« cilin direktiva Requires do tĂ« tregojĂ« nĂ« skedĂ«n unit tĂ« socket-it. MĂ« pas, nĂ« skedĂ«n unit tĂ« shĂ«rbimit, vetĂ«m duhet tĂ« thĂ«rrasim Gunicorn nga ambienti virtual dhe tĂ« krijojmĂ« njĂ« lidhje WSGI pĂ«r socket-in UNIX dhe aplikacionin Django.

Këtu janë disa shembuj të skedave unit, të cilat mund të shërbejnë si bazë. Së pari konfigurojmë socket-in.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=\/run\/gunicorn.sock

[Install]
WantedBy=sockets.target

Tani është e nevojshme të konfigurojmë demonin 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

Për Nginx, mjafton të krijoni skedarët e konfigurimit të proxy dhe të konfiguroheni direktorinë për ruajtjen e përmbajtjes statike, nëse e përdorni. Në RHEL, skedarët e konfigurimit të Nginx ndodhen në \/etc\/nginx\/conf.d. Mund të kopjoni këtë shembull në skedarin \/etc\/nginx\/conf.d\/default.conf dhe të nisni shërbimin. Sigurohuni që keni specifikuar server_name në përputhje me emrin e hostit tuaj.

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

Nisni socket-in Gunicorn dhe Nginx nëpërmjet systemd, dhe mund të filloni testimin.

Gabim Bad Gateway?

Nëse futni një adresë në shfletues, ka të ngjarë të merrni gabimin 502 Bad Gateway. Ai mund të shkaktohet nga lejet e pakonfiguruara për socket UNIX, ose nga probleme më të komplikuara që lidhen me menaxhimin e aksesit në SELinux.

Në regjistrin e gabimeve nginx mund të gjeni një rresht të ngjashëm:

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"

Nëse testoni Gunicorn direkt, do të merrni një përgjigje të zbrazët.

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

Le të shohim se pse ndodh kjo. Nëse hapim regjistrin, ka të ngjarë të shohim se problemi lidhet me SELinux. Duke qenë se kemi një demon të aktivizuar, për të cilin nuk është krijuar politika e tij, ai shënohet si init_t. Të verifikojmë këtë teori në praktikë.

sudo setenforce 0

Të gjithë kjo mund të shkaktojë kritika dhe lot të përgjakshëm, por është thjesht një debug i prototipit. Do ta çaktivizojmë verifikimin vetëm për të konfirmuar se problemi është pikërisht aty, pas së cilës do të kthejmë gjithçka në vendin e saj.

Pas rifreskimit të faqes në shfletues ose riaktivizimit të komandës sonë curl, mund të shohim faqen testuese të Django.

Pra, duke u siguruar që gjithçka funksionon dhe nuk ka më probleme me lejet, ne e aktivizojmë përsëri SELinux.

sudo setenforce 1

Këtu nuk do të flitet për audit2allow dhe për krijimin e politikave bazuar në njoftimet përmes sepolgen, sepse aktualisht nuk ka një aplikacion të vërtetë Django, kështu që nuk ka as një hartë të plotë të asaj që Gunicorn mund të dëshirojë të aksesojë dhe çfarë ky akses duhet të ndalohet. Prandaj, është e nevojshme të ruhet funksioni i SELinux për mbrojtjen e sistemit, duke lejuar në të njëjtën kohë që aplikacioni të startojë dhe të lërë mesazhe në regjistrin e auditi, për t'u përdorur më vonë për të krijuar një politikë të vërtetë.

Caktimi i domain-eve të lejuara (permissive domains)

Për domain-et e lejuara në SELinux nuk janë dëgjuar të gjithë, por për to nuk ka asgjë të re. Shumë madje kanë punuar me to, pa e kuptuar. Kur krijohet një politikë bazuar në njoftimet e auditi, politika e krijuar përbën një domain të lejuar. Le të provojmë të krejmë një politikë të thjeshtë lejuese.

Për të krijuar një domain të caktuar të lejuar për Gunicorn, nevojitet një politikë dhe gjithashtu do të duhet të etiketojmë skedarët përkatës. Për më tepër, nevojiten mjete për të grumbulluar politika të reja.

sudo yum install selinux-policy-devel

Mekanizmi i domain-eve tĂ« lejuara Ă«shtĂ« njĂ« mjet i shkĂ«lqyer pĂ«r tĂ« zbuluar probleme, veçanĂ«risht kur bĂ«het fjalĂ« pĂ«r aplikacione tĂ« personalizuara ose pĂ«r aplikacione qĂ« vijnĂ« pa politika tĂ« krijuara mĂ« parĂ«. NĂ« kĂ«tĂ« rast, politika e domain-it tĂ« lejuar pĂ«r Gunicorn do tĂ« jetĂ« sa mĂ« e thjeshtĂ« – do tĂ« deklarojmĂ« tipin kryesor (gunicorn_t), do tĂ« deklarojmĂ« tipin qĂ« do tĂ« pĂ«rdorim pĂ«r etiketimin e disa skedarĂ«ve ekzekutivĂ« (gunicorn_exec_t), dhe pastaj do tĂ« konfiguroni kalimin (transition) pĂ«r sistemin, nĂ« mĂ«nyrĂ« qĂ« tĂ« etiketoni nĂ« mĂ«nyrĂ« tĂ« saktĂ« proceset nĂ« ekzekutim. Rreshti pĂ«rfundimtar vendos politikĂ«n si tĂ« lejuar si parazgjedhje nĂ« momentin e ngarkimit tĂ« saj.

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;

Mund ta kompilohet këtë skedar politik dhe ta shtoni atë në sistem.

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

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

Le të kontrollojmë nëse SELinux po bllokon diçka tjetër, përveç asaj që i qaset demonit tonë të panjohur.

sudo ausearch -m AVC

type=AVC msg=audit(1545315977.237:1273): avc: denied { write } për 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 nuk i jep Nginx-it të shkruajë të dhëna në soketin UNIX të përdorur nga Gunicorn. Zakonisht në raste të tilla fillon të ndryshohen politikat, por përpara na pret të zgjidhim edhe detyra të tjera. Mund të ndryshojmë gjithashtu cilësimet e domain-it, duke e kthyer atë nga një domain kufizimi në një domain lejesh. Tani do ta transferojmë httpd_t në domainin e lejeve. Kjo do të sigurojë aksesin e nevojshëm për Nginx-in, dhe ne do të jemi në gjendje të vazhdojmë me punën e të nxjerrim.

sudo semanage permissive -a httpd_t

Pra, kur arritëm të ruajmë mbrojtjen e SELinux (në të vërtetë nuk duhet ta lëmë projektin me SELinux në modalitetin e kufizuar) dhe domainet e lejeve ngarkohen, është e nevojshme të zbulojmë se çfarë duhet të etiketohet si gunicorn_exec_t, në mënyrë që gjithçka të fillojë të funksionojë siç duhet. Le të provojmë të kontaktojmë faqen e internetit për të parë mesazhe të reja për kufizimet në akses.

sudo ausearch -m AVC -c gunicorn

ShumĂ« mesazhe tĂ« natyrĂ«s ‘comm=«gunicorn»’ mund tĂ« shihen qĂ« ekzekutojnĂ« veprime tĂ« ndryshme mbi skedarĂ«t nĂ« \/srv\/djangoapp, kĂ«shtu qĂ«, Ă«shtĂ« evidente se kjo Ă«shtĂ« njĂ« nga komandat qĂ« duhet tĂ« etiketojmĂ«.

Por përveç kësaj, shfaqet një mesazh si ky:

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

Nëse shikoni statusin e shërbimit gunicorn ose ekzekutoni komandën ps, nuk do të shihni ndonjë proces të aktivizuar. Duket se gunicorn po përpiqet të qaset në interpretorin Python në ambientin tonë virtualenv, ndoshta për të ekzekutuar skenat e punës (workers). Prandaj, tani do të etiketojmë këto dy skedarë ekzekutues dhe do të kontrollojmë nëse mund ta hapim faqen tonë testuese Django.

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

Nevojitet të rindezësh shërbimin gunicorn për të zgjedhur etiketën e re. Mund ta rindezni menjëherë ose ndaloni shërbimin dhe lejoni që soketi ta ndizë atë kur të hapni faqen në shfletues. Sigurohuni që proceset kanë marrë etiketat e duhura duke përdorur ps.

ps -efZ | grep gunicorn

Mos harroni më pas të krijoni një politikë të duhur SELinux!

Nëse shikoni mesazhet AVC tani, mesazhi i fundit përmban permissive=1 për të gjitha që lidhën me aplikacionin dhe permissive=0 për pjesën tjetër të sistemit. Nëse kuptoni se çfarë lloj qasje është e nevojshme për aplikacionin real, mund të gjeni më shpejt një mënyrë optimale për të zgjidhur probleme të tilla. Por deri atëherë, është më mirë që sistemi të jetë i mbrojtur, dhe të keni një auditim të qartë dhe të dobishëm për projektin Django.

sudo ausearch -m AVC

Kemi arritur!

Një projekt Django i funksionueshëm me frontend në Nginx dhe Gunicorn WSGI ka dalë. Ne kemi konfiguruar Python 3 dhe PostgreSQL 10 nga depozitat RHEL 8 Beta. Tani mund të vazhdojmë përpara dhe të krijojmë (apo thjesht të vendosim) aplikacione Django ose të studiojmë mjetet e tjera të disponueshme në RHEL 8 Beta për automatizimin e procesit të konfigurimit, rritjen e performancës, ose madje containerizimin e kësaj konfigurimi.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster