Workshop RHEL 8 Beta: Pagbuo ng gumaganang mga web application

Nag-aalok ang RHEL 8 Beta sa mga developer ng maraming bagong feature, ang listahan ng kung saan ay maaaring tumagal ng mga pahina, gayunpaman, ang pag-aaral ng mga bagong bagay ay palaging mas mahusay sa pagsasanay, kaya sa ibaba ay nag-aalok kami ng workshop sa aktwal na paglikha ng imprastraktura ng application batay sa Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Pagbuo ng gumaganang mga web application

Kunin natin ang Python, isang sikat na programming language sa mga developer, bilang batayan, isang kumbinasyon ng Django at PostgreSQL, isang medyo karaniwang kumbinasyon para sa paglikha ng mga application, at i-configure ang RHEL 8 Beta upang gumana sa kanila. Pagkatapos ay magdaragdag kami ng ilang higit pang (unclassified) na sangkap.

Magbabago ang kapaligiran ng pagsubok, dahil kawili-wiling tuklasin ang mga posibilidad ng automation, pagtatrabaho sa mga lalagyan at pagsubok ng mga kapaligiran na may maraming server. Upang makapagsimula sa isang bagong proyekto, maaari kang magsimula sa pamamagitan ng paggawa ng isang maliit, simpleng prototype sa pamamagitan ng kamay upang makita mo nang eksakto kung ano ang kailangang mangyari at kung paano ito nakikipag-ugnayan, at pagkatapos ay magpatuloy sa pag-automate at lumikha ng mas kumplikadong mga pagsasaayos. Ngayon ay pinag-uusapan natin ang paglikha ng naturang prototype.

Magsimula tayo sa pamamagitan ng pag-deploy ng RHEL 8 Beta VM na imahe. Maaari kang mag-install ng virtual machine mula sa simula, o gamitin ang KVM guest image na available sa iyong Beta subscription. Kapag gumagamit ng guest image, kakailanganin mong mag-configure ng virtual CD na maglalaman ng metadata at data ng user para sa cloud initialization (cloud-init). Hindi mo kailangang gumawa ng anumang espesyal sa istraktura ng disk o magagamit na mga pakete; magagawa ng anumang pagsasaayos.

Tingnan natin ang buong proseso.

Pag-install ng Django

Gamit ang pinakabagong bersyon ng Django, kakailanganin mo ng isang virtual na kapaligiran (virtualenv) na may Python 3.5 o mas bago. Sa Beta notes makikita mo na available ang Python 3.6, tingnan natin kung ito nga ang kaso:

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

Ang Red Hat ay aktibong gumagamit ng Python bilang isang toolkit ng system sa RHEL, kaya bakit ito nagreresulta?

Ang katotohanan ay maraming mga developer ng Python ang nag-iisip pa rin ng paglipat mula sa Python 2 hanggang Python 2, habang ang Python 3 mismo ay nasa ilalim ng aktibong pag-unlad, at parami nang parami ang mga bagong bersyon na patuloy na lumalabas. Samakatuwid, upang matugunan ang pangangailangan para sa mga stable na tool ng system habang nag-aalok sa mga user ng access sa iba't ibang bagong bersyon ng Python, ang system Python ay inilipat sa isang bagong pakete at nagbigay ng kakayahang mag-install ng parehong Python 2.7 at 3.6. Higit pang impormasyon tungkol sa mga pagbabago at kung bakit ginawa ang mga ito ay matatagpuan sa publikasyon sa Blog ni Langdon White (Langdon White).

Kaya, upang makakuha ng gumaganang Python, kailangan mo lamang mag-install ng dalawang pakete, kasama ang python3-pip bilang isang dependency.

sudo yum install python36 python3-virtualenv

Bakit hindi gumamit ng mga direktang module na tawag gaya ng iminumungkahi ni Langdon at mag-install ng pip3? Isinasaisip ang paparating na automation, alam na ang Ansible ay mangangailangan ng pip na naka-install upang tumakbo, dahil ang pip module ay hindi sumusuporta sa mga virtualenv na may custom na pip na maipapatupad.

Sa pamamagitan ng gumaganang python3 interpreter na magagamit mo, maaari kang magpatuloy sa proseso ng pag-install ng Django at magkaroon ng gumaganang sistema kasama ng aming iba pang mga bahagi. Mayroong maraming mga opsyon sa pagpapatupad na magagamit sa Internet. Mayroong isang bersyon na ipinakita dito, ngunit ang mga gumagamit ay maaaring gumamit ng kanilang sariling mga proseso.

I-install namin ang mga bersyon ng PostgreSQL at Nginx na available sa RHEL 8 bilang default gamit ang Yum.

sudo yum install nginx postgresql-server

Ang PostgreSQL ay mangangailangan ng psycopg2, ngunit kailangan itong maging available lamang sa isang virtualenv na kapaligiran, kaya i-install namin ito gamit ang pip3 kasama ng Django at Gunicorn. Ngunit kailangan muna nating i-set up ang virtualenv.

Palaging maraming debate sa paksa ng pagpili ng tamang lugar para mag-install ng mga proyekto ng Django, ngunit kapag may pag-aalinlangan, maaari mong palaging bumaling sa Linux Filesystem Hierarchy Standard. Sa partikular, sinasabi ng FHS na ang /srv ay ginagamit upang: β€œmag-imbak ng data na partikular sa hostβ€”data na ginagawa ng system, gaya ng data at script ng web server, data na nakaimbak sa mga FTP server, at kontrolin ang mga repositoryo ng system.” (lumalabas sa FHS -2.3 noong 2004).

Ito ang eksaktong kaso namin, kaya inilalagay namin ang lahat ng kailangan namin sa /srv, na pagmamay-ari ng aming user ng application (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

Ang pag-set up ng PostgreSQL at Django ay madali: lumikha ng database, lumikha ng user, mag-configure ng mga pahintulot. Ang isang bagay na dapat tandaan kapag unang ini-install ang PostgreSQL ay ang postgresql-setup script na naka-install kasama ang postgresql-server package. Tinutulungan ka ng script na ito na magsagawa ng mga pangunahing gawain na nauugnay sa pangangasiwa ng cluster ng database, tulad ng pagsisimula ng cluster o proseso ng pag-upgrade. Upang i-configure ang isang bagong instance ng PostgreSQL sa isang RHEL system, kailangan nating patakbuhin ang command:

sudo /usr/bin/postgresql-setup -initdb

Maaari mong simulan ang PostgreSQL gamit ang systemd, lumikha ng isang database, at mag-set up ng isang proyekto sa Django. Tandaang i-restart ang PostgreSQL pagkatapos gumawa ng mga pagbabago sa file ng pagsasaayos ng pagpapatotoo ng kliyente (karaniwang pg_hba.conf) upang i-configure ang storage ng password para sa user ng application. Kung makakaranas ka ng iba pang mga paghihirap, tiyaking baguhin ang mga setting ng IPv4 at IPv6 sa pg_hba.conf file.

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

Sa file /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

Sa file /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 }}',
   }
}

Pagkatapos i-configure ang settings.py file sa proyekto at i-set up ang configuration ng database, maaari mong simulan ang development server upang matiyak na gumagana ang lahat. Pagkatapos simulan ang development server, magandang ideya na lumikha ng admin user upang masubukan ang koneksyon sa database.

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

WSGI? Wai?

Ang development server ay kapaki-pakinabang para sa pagsubok, ngunit upang patakbuhin ang application dapat mong i-configure ang naaangkop na server at proxy para sa Web Server Gateway Interface (WSGI). Mayroong ilang mga karaniwang kumbinasyon, halimbawa, Apache HTTPD na may uWSGI o Nginx na may Gunicorn.

Ang trabaho ng Web Server Gateway Interface ay ipasa ang mga kahilingan mula sa web server sa Python web framework. Ang WSGI ay isang relic ng kakila-kilabot na nakaraan noong ang mga CGI engine ay nasa paligid, at ngayon ang WSGI ay ang de facto standard, anuman ang web server o Python framework na ginamit. Ngunit sa kabila ng malawakang paggamit nito, marami pa ring mga nuances kapag nagtatrabaho sa mga framework na ito, at maraming mga pagpipilian. Sa kasong ito, susubukan naming magtatag ng pakikipag-ugnayan sa pagitan ng Gunicorn at Nginx sa pamamagitan ng isang socket.

Dahil ang parehong bahaging ito ay naka-install sa parehong server, subukan nating gumamit ng UNIX socket sa halip na isang network socket. Dahil ang komunikasyon ay nangangailangan ng socket sa anumang kaso, subukan nating gumawa ng isa pang hakbang at i-configure ang socket activation para sa Gunicorn sa pamamagitan ng systemd.

Ang proseso ng paglikha ng socket activated services ay medyo simple. Una, ang isang unit file ay nilikha na naglalaman ng isang ListenStream directive na tumuturo sa punto kung saan ang UNIX socket ay gagawin, pagkatapos ay isang unit file para sa serbisyo kung saan ang Requires na direktiba ay ituturo sa socket unit file. Pagkatapos, sa file ng unit ng serbisyo, ang natitira ay tumawag sa Gunicorn mula sa virtual na kapaligiran at lumikha ng isang WSGI na nagbubuklod para sa UNIX socket at sa Django application.

Narito ang ilang halimbawa ng mga file ng unit na maaari mong gamitin bilang batayan. Una naming i-set up ang socket.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Ngayon ay kailangan mong i-configure ang 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

Para sa Nginx, ito ay isang simpleng bagay ng paglikha ng mga proxy configuration file at pag-set up ng isang direktoryo upang mag-imbak ng static na nilalaman kung gumagamit ka ng isa. Sa RHEL, ang mga file ng pagsasaayos ng Nginx ay matatagpuan sa /etc/nginx/conf.d. Maaari mong kopyahin ang sumusunod na halimbawa sa file /etc/nginx/conf.d/default.conf at simulan ang serbisyo. Tiyaking itakda ang server_name upang tumugma sa iyong 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;
   }
}

Simulan ang Gunicorn socket at Nginx gamit ang systemd at handa ka nang simulan ang pagsubok.

Bad Gateway error?

Kung ilalagay mo ang address sa iyong browser, malamang na makakatanggap ka ng 502 Bad Gateway error. Maaaring sanhi ito ng hindi wastong pagkaka-configure ng mga pahintulot ng UNIX socket, o maaaring dahil sa mas kumplikadong mga isyu na nauugnay sa kontrol sa pag-access sa SELinux.

Sa log ng error ng nginx maaari mong makita ang isang linya tulad nito:

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"

Kung direktang susubukan namin ang Gunicorn, makakakuha kami ng walang laman na sagot.

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

Alamin natin kung bakit ito nangyayari. Kung bubuksan mo ang log, malamang na makikita mo na ang problema ay nauugnay sa SELinux. Dahil nagpapatakbo kami ng isang daemon kung saan walang ginawang patakaran, minarkahan ito bilang init_t. Subukan natin ang teoryang ito sa pagsasanay.

sudo setenforce 0

Ang lahat ng ito ay maaaring maging sanhi ng pagpuna at luha ng dugo, ngunit ito ay pag-debug lamang sa prototype. I-disable natin ang check para lang masigurado na ito ang problema, pagkatapos ay ibabalik natin ang lahat sa lugar nito.

Sa pamamagitan ng pagre-refresh ng page sa browser o muling pagpapatakbo ng aming curl command, makikita mo ang Django test page.

Kaya, nang matiyak na gumagana ang lahat at wala nang mga problema sa pahintulot, muli naming pinagana ang SELinux.

sudo setenforce 1

Hindi ako magsasalita tungkol sa audit2allow o paglikha ng mga patakarang nakabatay sa alerto na may sepolgen dito, dahil walang aktwal na aplikasyon ng Django sa ngayon, kaya walang kumpletong mapa kung ano ang gustong i-access ng Gunicorn at kung ano ang dapat nitong tanggihan ang pag-access. Samakatuwid, kinakailangan na panatilihing tumatakbo ang SELinux upang maprotektahan ang system, habang pinapayagan ang application na tumakbo at mag-iwan ng mga mensahe sa audit log upang ang aktwal na patakaran ay malikha mula sa kanila.

Pagtukoy ng mga pinahihintulutang domain

Hindi lahat ay nakarinig ng mga pinapayagang domain sa SELinux, ngunit hindi na bago ang mga ito. Marami pa ngang nakatrabaho sa kanila nang hindi man lang namamalayan. Kapag ginawa ang isang patakaran batay sa mga mensahe ng pag-audit, kinakatawan ng ginawang patakaran ang nalutas na domain. Subukan nating lumikha ng isang simpleng patakaran sa pagpapahintulot.

Upang lumikha ng isang partikular na pinapayagang domain para sa Gunicorn, kailangan mo ng ilang uri ng patakaran, at kailangan mo ring markahan ang mga naaangkop na file. Bilang karagdagan, kailangan ang mga tool upang bumuo ng mga bagong patakaran.

sudo yum install selinux-policy-devel

Ang mekanismo ng pinapayagang domain ay isang mahusay na tool para sa pagtukoy ng mga problema, lalo na pagdating sa isang custom na application o mga application na ipinapadala nang walang mga patakarang nagawa na. Sa kasong ito, ang pinahihintulutang patakaran ng domain para sa Gunicorn ay magiging simple hangga't maaari - magdeklara ng pangunahing uri (gunicorn_t), magdeklara ng uri na gagamitin namin para markahan ang maramihang mga executable (gunicorn_exec_t), at pagkatapos ay mag-set up ng transition para sa system na tama na markahan tumatakbong mga proseso. Itinatakda ng huling linya ang patakaran bilang naka-enable bilang default sa oras na na-load ito.

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;

Maaari mong i-compile ang file ng patakaran na ito at idagdag ito sa iyong system.

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

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

Tingnan natin kung may iba pang hinaharangan ang SELinux maliban sa ina-access ng ating hindi kilalang daemon.

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

Pinipigilan ng SELinux ang Nginx sa pagsusulat ng data sa UNIX socket na ginagamit ng Gunicorn. Karaniwan, sa ganitong mga kaso, ang mga patakaran ay nagsisimulang magbago, ngunit may iba pang mga hamon sa hinaharap. Maaari mo ring baguhin ang mga setting ng domain mula sa isang restriction domain patungo sa isang domain ng pahintulot. Ngayon, ilipat natin ang httpd_t sa domain ng mga pahintulot. Bibigyan nito ang Nginx ng kinakailangang pag-access at maaari tayong magpatuloy sa karagdagang pag-debug.

sudo semanage permissive -a httpd_t

Kaya, kapag nagawa mong panatilihing protektado ang SELinux (talagang hindi mo dapat iwanan ang isang proyekto ng SELinux sa restricted mode) at ang mga domain ng pahintulot ay na-load, kailangan mong malaman kung ano ang eksaktong kailangang markahan bilang gunicorn_exec_t upang gumana nang maayos ang lahat. muli. Subukan nating bisitahin ang website upang makakita ng mga bagong mensahe tungkol sa mga paghihigpit sa pag-access.

sudo ausearch -m AVC -c gunicorn

Makakakita ka ng maraming mensahe na naglalaman ng 'comm="gunicorn"' na gumagawa ng iba't ibang bagay sa mga file sa /srv/djangoapp, kaya malinaw na isa ito sa mga utos na dapat i-flag.

Ngunit bilang karagdagan, lumilitaw ang isang mensahe tulad nito:

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

Kung titingnan mo ang katayuan ng serbisyo ng gunicorn o patakbuhin ang utos ng ps, hindi ka makakakita ng anumang mga prosesong tumatakbo. Mukhang sinusubukan ng gunicorn na i-access ang Python interpreter sa aming virtualenv environment, posibleng magpatakbo ng mga script ng manggagawa. Kaya ngayon, markahan natin ang dalawang executable na file na ito at tingnan kung mabubuksan natin ang aming Django test page.

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

Ang serbisyo ng gunicorn ay kailangang i-restart bago mapili ang bagong tag. Maaari mo itong i-restart kaagad o ihinto ang serbisyo at hayaang simulan ito ng socket kapag binuksan mo ang site sa browser. I-verify na ang mga proseso ay nakatanggap ng mga tamang label gamit ang ps.

ps -efZ | grep gunicorn

Huwag kalimutang gumawa ng normal na patakaran ng SELinux mamaya!

Kung titingnan mo ang mga mensahe ng AVC ngayon, ang huling mensahe ay naglalaman ng permissive=1 para sa lahat ng nauugnay sa application, at permissive=0 para sa natitirang bahagi ng system. Kung naiintindihan mo kung anong uri ng pag-access ang kailangan ng isang tunay na application, mabilis mong mahahanap ang pinakamahusay na paraan upang malutas ang mga naturang problema. Ngunit hanggang doon, pinakamahusay na panatilihing secure ang system at makakuha ng malinaw, magagamit na pag-audit ng proyekto ng Django.

sudo ausearch -m AVC

Nangyari!

Ang isang gumaganang proyekto ng Django ay lumitaw na may frontend batay sa Nginx at Gunicorn WSGI. Na-configure namin ang Python 3 at PostgreSQL 10 mula sa mga repositoryo ng RHEL 8 Beta. Ngayon ay maaari ka nang sumulong at lumikha (o mag-deploy lang) ng mga Django application o mag-explore ng iba pang available na tool sa RHEL 8 Beta upang i-automate ang proseso ng pagsasaayos, pagbutihin ang pagganap, o kahit na i-container ang configuration na ito.

Pinagmulan: www.habr.com

Magdagdag ng komento