Praktikumi RHEL 8 Beta: Ndërtojmë aplikacione web funksionale

RHEL 8 Beta ofron zhvilluesve shumë mundësi të reja, lista e të cilave mund të zërë faqe, megjithatë, është më mirë të mësohet e reja në praktikë, prandaj më poshtë ju ftojmë të kaloni në një praktikum të krijimit të infrastrukturës së aplikacioneve mbi Red Hat Enterprise Linux 8 Beta.

Praktikumi RHEL 8 Beta: Ndërtojmë aplikacione web funksionale

Ne do të marrim si bazë Python, një gjuhë programimi të njohur mes zhvilluesve, kombinimin Django dhe PostgreSQL, një kombinim të zakonshëm për krijimin e aplikacioneve, dhe do të konfigurim RHEL 8 Beta për të punuar me to. Pastaj do të shtojmë disa përbërës (të padëshiruar).

Mjedisi i testimit do të ndryshojë, sepse është interesante të studiohen mundësitë e automatizimit, puna me kontejnerët dhe të provojmë mjedise me disa serverë. Për të filluar punën me projektin e ri, mund të filloni me krijimin e një prototipi të thjeshtë manualisht – kështu mund të shihni se çfarë duhet të ndodhë dhe si ndodh ndërveprimi, dhe pastaj të kaloni në automatizim dhe krijimin e konfigurimeve më të komplikuara. Sot do flas për krijimin e një prototipi të tillë.

Le të fillojmë me zbatimin e imazhit të makinerisë virtuale RHEL 8 Beta VM. Mund të instaloni makinerinë virtuale nga e para, ose të përdorni imazhin mysafir KVM, që është në dispozicion me abonimin për Beta. Kur përdorni imazhin mysafir, do të jetë e nevojshme të konfiguroni një CD virtual, i cili do të përmbajë metadatash dhe të dhëna për inicializimin e retnesuar (cloud-init). Nuk ka nevojë të bëni ndonjë gjë të veçantë me strukturën e disku ose paketat e disponueshme, ndonjë konfigurim do të bëjë.

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

Instalimi i Django

Me versionin më të ri të Django do të nevojitet një mjedis virtual (virtualenv) me Python 3.5 ose një version më të fundit. Në shënimet për Beta mund të shihni se Python 3.6 është në dispozicion, le të verifikojmë nëse është e vërtetë:

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

Red Hat e përdor në mënyrë aktive Python si një mjet sistemor në RHEL, pra pse ndodh ky rezultat?

Fakti është se shumë zhvillues që përdorin Python, ende po mendojnë për kalimin nga Python 2 në Python 2, ndërsa vetë Python 3 është në fazën e zhvillimit aktiv dhe vazhdimisht po dalin versione të reja. Prandaj, për të përmbushur nevojën për mjete sistemorë të stabilizuar, dhe njëkohësisht t'u ofrojë përdoruesve qasje në versione të ndryshme të reja të Python, Python-i sistemor është transferuar në një paketë të re dhe është siguruar mundësia e instalimit si Python 2.7, ashtu edhe 3.6. Më shumë informacion mbi ndryshimet dhe arsyen pse kjo u bë mund të gjendet në publikimin në blogun e Langdon White (Langdon White).

Prandaj, për të marrë një Python që punon, nevojitet të instaloni vetëm dy pena, ndërkohë që python3-pip do të instalohet si varësi.

sudo yum install python36 python3-virtualenv

Pse nuk duhet të përdoren thirrjet direkte në modul, siç sugjeron Langdon, dhe të mos instalohet pip3? Duke pasur parasysh automatizimin e ardhshëm, dihet se Ansible kërkon të ketë instaluar pip, pasi moduli pip nuk përkrah mjediset virtuale (virtualenvs) me një ekzekutues të personalizuar pip.

Duke pasur në dorë interpretorin e punës python3, mund të vazhdojmë me procesin e instalimit të Django dhe të kemi një sistem në funksion me komponentët tanë të tjerë. Ekzistojnë shumë mundësi për realizimin e këtij procesi. Këtu është një version, por përdoruesit mund të përdorin proceset e tyre.

Versionet e PostgreSQL dhe Nginx, të disponueshme në RHEL 8, do të instalohen me Yum.

sudo yum install nginx postgresql-server

Për PostgreSQL do të nevojitet psycopg2, por për të qenë në dispozicion vetëm në ambientin virtualenv, do ta instalojmë atë me pip3 së bashku me Django dhe Gunicorn. Por së pari, duhet të konfigurojmë virtualenv.

Për zgjidhjen e vendndodhjes së duhur për instalimin e projekteve Django është bërë gjithmonë shumë diskutim, por, kur ka dyshime, gjithmonë mund të referoheni standardeve të Linux në Hierarchy File System Standard. Në veçanti, në FHS thuhet se /srv përdoret për: 'ruajtjen e të dhënave specifike të nodit, të dhëna që jep sistemi, si p.sh. të dhënat dhe skedarët e serverëve të uebit, të dhënat e ruajtura në serverë FTP, si dhe depozitë të sistemeve të kontrollit të versioneve (të cilat kanë dalë në FHS-2.3 në vitin 2004)'.

Kjo është pikërisht rasti ynë, prandaj do të vendosim gjithçka të nevojshme në /srv, e cila është nën pronësinë e përdoruesit tonë të 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 paraqet vështirësi: krijojmë një bazë të dhënash, krijojmë përdorues, konfigurimin e lejeve. Ka një moment që duhet të mbani mend gjatë instalimit fillestar të PostgreSQL – është skripti postgresql-setup, i cili instalohet së bashku me paketën postgresql-server. Ky skript ndihmon në kryerjen e detyrave themelore që lidhen me administrimin e klasterit të bazës së të dhënave, siç janë inicializimi i klasterit ose procesi i 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 duke përdorur systemd, të krijoni bazën e të dhënave dhe të konfiguroni projektin në Django. Mos harroni të riblini PostgreSQL pas ndërrimeve në skedarin e konfigurimit të autentifikimit të klientëve (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ë cilësimet IPv4 dhe IPv6 në skedarin pg_hba.conf janë ndryshuar.

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ë filloni serverin e zhvillimit për të siguruar që gjithçka funksionon. Pas fillimit të serverit të zhvillimit, është e dobishme 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ë ekzekutuar aplikacionin duhet të konfigurohet serveri dhe proksi i duhur për Web Server Gateway Interface (WSGI). Ka disa kombinime të zakonshme, si Apache HTTPD me uWSGI ose Nginx me Gunicorn.

Detyra e Web Server Gateway Interface është të drejtojë kërkesat nga web në frameworkun web Python. WSGI është një trashëgimi e një të kaluare të tmerrshme, kur ishin në përdorim mekanizmat CGI, dhe sot WSGI është në të vërtetë standardi, pa marrë parasysh serverin web ose frameworkun Python që përdoret. Por, megjithë përhapjen e tij, ende ekzistojnë shumë nuanca kur punoni me këto framework-e, dhe shumë mundësi zgjedhjeje. Në këtë rast, ne do të përpiqemi të vendosim komunikimin mes Gunicorn dhe Nginx përmes soketëve.

Duke qenë se këto dy komponentë janë instaluar në të njëjtin server, do të përpiqemi të përdorim një soket UNIX në vend të soketëve rrjetë. Duke qenë se për komunikimin gjithsesi na nevojitet një soket, do të përpiqemi të bëjmë një hap tjetër dhe të konfigurojmë aktivizimin e soketit për Gunicorn përmes systemd.

Procesi i krijimit të shërbimeve të aktivizuara nga soketet është mjaft i thjeshtë. Së pari krijohet një skedë unit, e cila përmban direktivën ListenStream, e cila tregon pikën ku do të krijohet soketi UNIX, dhe mandej – skedën për shërbimin, në të cilën direktiva Requires do të tregohet në skedën e soketit. Pasi të krijohet skeda e shërbimit, mbetet vetëm të thërrasë Gunicorn nga ambienti virtual dhe të krijojë një lidhje WSGI për soketin UNIX dhe aplikacionin Django.

Ja disa shembuj skedash unit që mund të shërbejnë si bazë. Së pari konfiguroni soketin.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Tani është e nevojshme të konfigurohet demon 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ë proksisë dhe të konfiguroni drejtorinë 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 ta 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 soketin Gunicorn dhe Nginx duke përdorur systemd, dhe mund të filloni testimin.

Gabimi Bad Gateway?

Nëse hyni adresën në shfletues, ka shumë mundësi që të merrni gabimin 502 Bad Gateway. Ky mund të shkaktohet nga lejet e papërshtatshme për soketin UNIX, ose nga probleme më të komplikuara që lidhen me menaxhimin e qasjes në SELinux.

Në regjistrin e gabimeve nginx mund të shihni një rresht të këtij lloji:

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"

Если тестировать Gunicorn напрямую, то получим пустой ответ.

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

Давайте разберемся, почему это происходит. Если открыть журнал, то вероятнее всего, увидим, что проблема связана с SELinux. Поскольку у нас запущен демон, для которого не было создано своей политики, он помечается как init_t. Проверим эту теорию на практике.

sudo setenforce 0

Все это может вызвать критику и кровавые слезы, но это всего лишь отладка прототипа. Отключим проверку лишь чтобы убедиться, что проблема именно в этом, после чего вернем все назад на свои места.

Обновив страницу в браузере или перезапустив нашу команду curl, можно лицезреть тестовую страницу Django.

Итак, убедившись, что все работает, и больше никаких проблем с разрешениями нет, мы вновь включаем SELinux.

sudo setenforce 1

Здесь не будет рассказа об audit2allow и о создании политик на основе оповещений с помощью sepolgen, поскольку на данный момент нет реального приложения Django, то нет и полной карты того, к чему Gunicorn может захотеть получить доступ, и к чему этот доступ следует запретить. Поэтому необходимо сохранить работу SELinux для защиты системы, и в то же время разрешить приложению запускаться и оставлять сообщения в журнале аудита, чтобы можно было затем на их основе создать реальную политику.

Указание разрешенных доменов (permissive domains)

О разрешенных доменах в SELinux не все слышали, но в них нет ничего нового. Многие даже работали с ними, сами об этом не догадываясь. Когда создается политика на основе сообщений аудита, созданная политика представляет собой разрешенный домен. Попробуем создать простейшую разрешительную политику.

Чтобы создать конкретный разрешенный домен для Gunicorn, нужна некая политика, а также потребуется пометить соответствующие файлы. Кроме того, необходимы инструменты, чтобы собрать новые политики.

sudo yum install selinux-policy-devel

Механизм разрешенных доменов – прекрасный инструмент для выявления проблем, особенно когда речь идет о кастомном приложении или о приложениях, которые поставляются без уже созданных политик. В данном случае политика разрешенного домена для Gunicorn будет максимально простой – объявим основной тип (gunicorn_t), объявим тип, который мы будем использовать для пометки нескольких исполняемых файлов (gunicorn_exec_t), и затем настроим переход (transition) для system, чтобы корректно отмечать запущенные процессы. Последняя строчка устанавливает политику как разрешенную по умолчанию на момент ее загрузки.

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;

Можно скомпилировать этот файл политик и добавить его в систему.

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

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

Давайте проверим, не блокирует ли SELinux что-то еще, помимо того, к чему обращается наш неизвестный демон.

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 не дает Nginx записывать данные в сокет UNIX, используемый Gunicorn. Обычно в таких случаях начинают менять политики, но впереди предстоит решить и другие задачи. Можно также изменить настройки домена, превратив его из домена ограничений в домен разрешений. Теперь перенесем httpd_t в домен разрешений. Это обеспечит Nginx необходимый доступ, и мы сможем продолжить дальнейшую работу по отладке.

sudo semanage permissive -a httpd_t

Итак, когда удалось сохранить защиту SELinux (на самом деле не следует оставлять проект с SELinux в режиме ограничений) и домены разрешений загружаются, необходимо выяснить, что именно нужно пометить как gunicorn_exec_t, чтобы все вновь заработало как положено. Попробуем обратиться к веб-сайту, чтобы увидеть новые сообщения об ограничениях в доступе.

sudo ausearch -m AVC -c gunicorn

Можно видеть множество сообщений, содержащих ‘comm=«gunicorn»’, которые выполняют различные действия над файлами в /srv/djangoapp, поэтому, очевидно, это как раз одна из команд, которую стоит пометить.

Но кроме того, появляется сообщение вот такого вида:

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 asnjë proces të startuar. Duket se gunicorn po përpiqet të lidhet me interpretuesin Python në mjedisin tonë virtualenv, ndoshta për të ekzekutuar skenaret e punës (workers). Prandaj, tani do të shënojmë këto dy skedarë ekzekutues dhe do të kontrollojmë nëse mund të hapim faqen tonë të testit Django.

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

Do të jetë e nevojshme të ri-startoni shërbimin gunicorn për të zgjedhur etiketën e re. Mund ta ri-startoni menjëherë ose ta ndaloni shërbimin dhe ta lejoni soketin ta startojë atë kur të hapni faqen në shfletues. Sigurohuni që proceset të kenë marrë etiketat e nevojshme, duke përdorur ps.

ps -efZ | grep gunicorn

Mos harroni pastaj të krijoni një politikë normale SELinux!

Nëse shikoni mesazhet AVC tani, mesazhi i fundit përmban permissive=1 për gjithçka që lidhet me aplikacionin dhe permissive=0 për pjesën tjetër të sistemit. Nëse kuptoni se çfarë qasjeje kërkon një aplikacion i vërtetë, 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ë audit të qartë dhe të dobishëm për projektin Django.

sudo ausearch -m AVC

Kemi arritur!

Tani kemi një projekt funksional Django me frontin në Nginx dhe Gunicorn WSGI. 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ë shpërndajmë) aplikacione Django ose të studiojmë mjete të tjera të disponueshme në RHEL 8 Beta për automatizimin e procesit të konfigurimit, përmirësimin e performancës ose madje edhe kontenjerizimin e kësaj konfigurimi.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster