RHEL 8 Beta praktikum: Loome töötavaid veebirakendusi

RHEL 8 Beta pakub arendajatele palju uusi võimalusi, mille loetlemine võiks võtta lehekülgede kaupa, kuid uut on alati parem õppida praktiliselt. Seetõttu pakume allpool reaalset praktikumi rakenduste infrastruktuuri loomise kohta Red Hat Enterprise Linux 8 Beta põhjal.

RHEL 8 Beta praktikum: Loome töötavaid veebirakendusi

Võtame aluseks Python'i, mis on arendajate seas populaarne programmeerimiskeel, Django ja PostgreSQL kombinatsiooni, mis on üsna levinud rakenduste loomise jaoks, ja konfigureerime RHEL 8 Beta nendega töötamiseks. Seejärel lisame veel mõned (mitte saladuslikud) koostisosad.

Testimiskeskkond muutub, kuna on huvitav uurida automatiseerimise võimalusi, töötamist konteineritega ja proovida mitme serveriga keskkondi. Uue projektiga alustamiseks võib kõigepealt luua käsitsi väikese lihtsa prototüübi – nii saab näha, mis peaks juhtuma ja kuidas interaktsioon toimub, ning seejärel liikuda automatiseerimise ja keerukamate konfiguratsioonide loomise juurde. Täna räägime sellise prototüübi loomisest.

Alustame RHEL 8 Beta VM pildi juurutamisest. Virtuaalmasina võib installida nullist või kasutada KVM külgmist pilti, mis on saadaval koos Beta tellimusega. Külgse pildi kasutamisel on vaja seadistada virtuaalne CD, mis sisaldab metainfot ja kasutajateavet pilve initsialiseerimiseks (cloud-init). Diskistruktuuri või saadaolevate paketiga ei pea midagi erilist tegema, sobib iga konfiguratsioon.

Vaatame kogu protsessi põhjalikumalt.

Django installimine

Uueim versioon Django'st vajab virtuaalset keskkonda (virtualenv) koos Python 3.5 või uuema versiooniga. Beta märkustes on öeldud, et Python 3.6 on saadaval, vaatame, kas see on tõesti nii:

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

Red Hat kasutab aktiivselt Pythonit süsteemse tööriistana RHEL-is, miks aga saadakse selline tulemus?

Asi on selles, et paljud Pythonit kasutavad arendajad mõtlevad endiselt üleminekule Python 2-lt Python 3-le, samas kui Python 3 on pidevas aktiivses arenduses ning uut versioonid ilmuvad pidevalt. Seetõttu, et rahuldada vajadust stabiilsete süsteemitööriistade järele ja samal ajal pakkuda kasutajatele juurdepääsu erinevatele uutele Python versioonidele, viidi süsteemi Python uude paketti ning on võimalik paigaldada nii Python 2.7 kui ka 3.6. Üksikasjalikku teavet muutuste kohta ja miks seda tehti, saab lugeda Langdon White'i blogist (Langdon White).

Seega, et saada töötav Python, tuleb installida vaid kaks paketti, kusjuures python3-pip tõmmatakse sõltuvusena.

sudo yum install python36 python3-virtualenv

Miks mitte kasutada otseseid viiteid moodulile, nagu soovitab Langdon, ja mitte installida pip3? Pidades silmas eelseisvat automatiseerimist, on teada, et Ansible'i tööks on vajalik paigaldatud pip, kuna pip-i moodul ei toeta virtuaalseid keskkondi (virtualenvs) kohandatud pipi täitmiseks.

Olles saanud töötava python3 tõlkija, saame jätkata Django installimise protsessi ja saada toimiva süsteemi koos meie teiste komponentidega. Internetis on palju rakendusvõimalusi. Siin on esitatud üks versioon, kuid kasutajad võivad kasutada oma tööprotsesse.

PostgreSQL ja Nginx versioonid, mis on RHEL 8-s vaikimisi saadaval, paigaldame Yum'i abil.

sudo yum install nginx postgresql-server

PostgreSQL jaoks on vajalik psycopg2, kuid see peab olema saadaval ainult virtuaalses keskkonnas, seega paigaldame selle koos Django ja Gunicorn'iga pip3 abil. Kuid kõigepealt peame seadistama virtuaalse keskkonna.

Django projektide paigalduskoha õige valiku üle käib alati palju vaidlusi, kuid kui tekib kahtlusi, võib alati tugineda Linuxi Failisüsteemi Hierarhiastandardile (FHS). Eelkõige on FHS's kirjas, et /srv-d kasutatakse: „andmete hoidmiseks, mis on spetsiifilised konkreetsele sõlmele, – andmed, mida süsteem väljastab, näiteks andmed ja skriptid veebiserverites, FTP serveritesse hoitavad andmed ning ka versioonihalduse hoidlad (mis ilmusid FHS-2.3 2004. aastal)”.

See on täpselt meie juhtum, seega paneme kõik vajaliku /srv kausta, mille omanikuks on meie rakenduse kasutaja (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

PostgreSQL ja Django seadistamine ei ole keeruline: loome andmebaasi, loome kasutaja, seadistame õigused. On üks asi, mida tuleb meeles pidada PostgreSQL algse installimise korral – see on skript postgresql-setup, mis installitakse koos postgresql-server paketiga. See skript aitab teostada põhitegevusi, mis on seotud andmebaasi klastrite haldamisega, näiteks klastrite algatamise või värskendamise protsessi. Uue PostgreSQL instantsi seadistamiseks RHEL süsteemis peame käitama käsku:

sudo /usr/bin/postgresql-setup -initdb

Pärast seda saame käivitada PostgreSQL systemd'i abil, luua andmebaasi ja seadistada projekti Django-s. Ärge unustage PostgreSQL uuesti käivitada pärast muudatuste tegemist kliendi autentimise konfiguratsioonifailis (tavaliselt pg_hba.conf), et seadistada rakenduse kasutaja parooli salvestamine. Kui teil tekib muid probleeme, veenduge, et IPv4 ja IPv6 seaded on muutunud pg_hba.conf failis.

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

Failis /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

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

Pärast settings.py faili konfigureerimist projektis ja andmebaasi konfiguratsiooni seadistamist, saame käivitada arendusseadmestiku, et veenduda, et kõik töötab. Pärast arendusseadmestiku käivitamist oleks hea luua admin kasutaja, et testida andmebaasi ühendust.

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

WSGI? Mis see on?

Arendusseade on kasulik testimiseks, kuid rakenduse käivitamiseks tuleb seadistada vastav server ja vaheline server Web Server Gateway Interface (WSGI) jaoks. On olemas mitmeid levinud kombinatsioone, näiteks Apache HTTPD koos uWSGI või Nginx koos Gunicorniga.

Web Server Gateway Interface'i ülesanne on suunata päringud veebiserver Python veebiraamistiku poole. WSGI on teatav pärand kohutavast minevikust, kui olid kasutusel CGI mehhanismid, ja tänapäeval on WSGI praktiliselt standard, sõltumata sellest, millist veebiserverit või Python raamistikku kasutatakse. Kuid hoolimata selle laialdasest kasutamisest on siiski palju nüansse nende raamistikuga töötamisel ning palju valikuvõimalusi. Sel juhul püüame seadistada Gunicorn'i ja Nginx'i koostööd soketi kaudu.

Kuna mõlemad komponendid on paigaldatud samasse serverisse, proovime kasutada võrgu soketi asemel UNIX soketti. Kuna suhtlemiseks on igal juhul vaja soketti, püüame astuda veel ühe sammu ja seadistada Gunicorn'i soketi aktiveerimist läbi systemd.

Soketiga aktiveeritud teenuste loomise protsess on üsna lihtne. Esiteks luuakse unit-fail, mis sisaldab direktiivi ListenStream, näidates punkti, kus UNIX sokett luuakse, seejärel — unit-fail teenuse jaoks, mille direktiiv Requires osutab soketi unit-failile. Seejärel jääb teenuse unit-failis ainult kutsuda Gunicorn'i välja virtuaalsest keskkonnast ja luua WSGI sidumine UNIX soketi ja Django rakenduse jaoks.

Siin on mõned näited unit-failidest, mida saab võtta aluseks. Esiteks seadistame soketi.

[Unit]
Description=Gunicorn WSGI sokett

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Nüüd tuleb seadistada Gunicorn deemon.

[Unit]
Description=Gunicorn deemon
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

Nginx'i jaoks piisab, kui luua lihtsalt proxy konfiguratsioonifailid ja seadistada kaust staatilise sisu jaoks, kui te seda kasutate. RHEL-is asuvad Nginx'i konfiguratsioonifailid /etc/nginx/conf.d. Saate kopeerida järgmise näite faili /etc/nginx/conf.d/default.conf ja käivitada teenuse. Veenduge, et olete määranud server_name vastavalt teie hosti nimele.

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

Käivita Gunicorn'i ja Nginx'i sokett systemd'i kaudu ning saad alustada testimist.

Viga Bad Gateway?

Kui sisestate aadressi brauserisse, saate tõenäoliselt 502 Bad Gateway vea. See võib olla tingitud valesti seadistatud õigustest UNIX soketile või keerukamatest probleemidest, mis on seotud SELinux'i juurdepääsu haldamisega.

Nginx'i vealogis võite leida järgmise sarnase rea:

2018/12/18 15:38:03 [crit] 12734#0: *3 ühendus() unix: /run/gunicorn.sock ebaõnnestus (13: Juhtimisõigus keelatud) upstream'i ühendamisel, klient: 192.168.122.1, server: 8beta1.example.com, päring: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", host: "8beta1.example.com"

Kui testida Gunicorn'i otse, saame tühja vastuse.

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

Vaatame, miks see juhtub. Kui avada logi, näeme tõenäoliselt, et probleem on seotud SELinux'iga. Kuna meil on käimas demon, mille jaoks ei looda oma poliitikat, märgitakse see kui init_t. Kontrollime seda teooriat praktikas.

sudo setenforce 0

Kõik see võib põhjustada kriitikat ja verd halavat nutu, kuid see on vaid prototüübi silumine. Lülitame kontrolli välja, et veenduda, et probleem on tõepoolest selles, pärast mida paneme kõik tagasi endisele kohale.

Brauserit uuendades või meie curl käsku uuesti käivitades saame näha Django testlehte.

Nii et nüüd, kui oleme veendunud, et kõik töötab ja rohkem pole õiguste probleeme, lubame SELinux'i uuesti sisse.

sudo setenforce 1

Siin ei räägita audit2allow'st ja poliitikate loomisest teavituste põhjal sepolgen'i abil, kuna praegu pole tegelikku Django rakendust ning seega pole ka täielikku kaarti sellest, millele Gunicorn võib juurdepääsu taotleda ja millele see juurdepääs tuleks keelduda. Seetõttu on vajalik säilitada SELinux'i töö süsteemi kaitsmiseks ja samal ajal lubada rakendusel käivituda ning jätta kirju auditi logisse, et saaksime hiljem nende põhjal luua tõelise poliitika.

Lubatud domeenide määramine (permissive domains)

Paljuski pole SELinux'is lubatud domeenidest teada, kuid need pole midagi uut. Paljud on isegi nendega töötanud, ilma et sellest teada oleksid. Kui luuakse poliitika auditi teavituste põhjal, on loodud poliitika lubatud domeen. Proovime luua kõige lihtsama lubava poliitika.

Gunicorn'ile konkreetse lubatud domeeni loomiseks on vajalik mingi poliitika, samuti tuleb vastavad failid märgistada. Lisaks on vajalikud tööriistad, et koguda uusi poliitikaid.

sudo yum install selinux-policy-devel

Lubatud domeenide mehhanism on suurepärane tööriist probleemide tuvastamiseks, eriti kui tegu on kohandatud rakenduse või rakendustega, mis tarnitakse juba loodud poliitikateta. Antud juhul on Gunicorn'i lubatud domeeni poliitika maksimaalselt lihtne - deklareerime põhityübi (gunicorn_t), deklareerime tüübi, mida kasutame mitme täitevfaili märgistamiseks (gunicorn_exec_t), ning seejärel seadistame ülemineku (transition) süsteemile, et korrektselt märgistada käivitatud protsessid. Viimane rida määrab poliitikat kui lubatud vaikealusena selle laadimise hetkel.

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;

Seda poliitikafaili on võimalik kompileerida ja süsteemi lisada.

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

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

Kontrollime, kas SELinux blokeerib midagi muud peale selle, millele meie tundmatu demon viitab.

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 ei luba Nginx'il kirjutada andmeid Gunicorn'i kasutatavale UNIX sokkile. Tavaliselt alustatakse poliitikate muutmisest, kuid ees on veel teised ülesanded. Samuti on võimalik domeeni seadeid muuta, muutes selle piiratud domeenist lubatavaks domeeniks. Nüüd muundame httpd_t lubatavaks domeeniks. See tagab Nginx'ile vajaliku juurdepääsu ja saame jätkata silumist.

sudo semanage permissive -a httpd_t

Nii, kui õnnestub säilitada SELinux'i kaitset (tõepoolest ei tohiks projekti jätta SELinux'i piirangute režiimi) ja lubatavad domeenid laaditakse, tuleb välja selgitada, mida täpselt tuleb märkida gunicorn_exec_t osana, et kõik uuesti õigesti töötaks. Proovime külastada veebisaiti, et näha uusi juurdepääsupiirangute sõnumeid.

sudo ausearch -m AVC -c gunicorn

Nähakse paljusid sõnumeid, mis sisaldavad 'comm=«gunicorn»', mis teostavad erinevaid toiminguid failidega /srv/djangoapp, seega on ilmselge, et see on üks käskudest, mida tasuks märgistada.

Kuid lisaks sellele ilmub sõnum järgmise kujuga:

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

Kui vaadata gunicorn teenuse staatust või käivitada ps käsk, siis ei ilmne mingeid töötavaid protsesse. Tundub, et gunicorn üritab pöörduda meie virtualenv keskkonnas Python tõlgendi poole, tõenäoliselt töövõtjate (workers) käivitamiseks. Seega märgime praegu need kaks käivitatavat faili ja kontrollime, kas suudame avada meie Django testlehte.

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

Gunicorn teenus tuleb taaskäivitada, et uus silt valitaks. Seda saab taaskäivitada kohe või peatada teenuse, lastes sokkil käivitada see, kui avada veebileht brauseris. Veenduge, et protsessid on saanud vajalikud sildid, kasutades ps.

ps -efZ | grep gunicorn

Ärge unustage seejärel luua õige SELinux poliitika!

Kui vaadata praeguseid AVC sõnumeid, siis viimane sõnum sisaldab permissive=1 kõigi rakendusega seotud asjade jaoks ja permissive=0 kogu ülejäänud süsteemi jaoks. Kui mõista, millist juurdepääsu tõeline rakendus vajab, on võimalik kiiremini leida optimaalseid lahendusi sellistele probleemidele. Kuid seni on parem, et süsteem oleks kaitstud, et saada arusaadav ja kasutatav audit Django projektist.

sudo ausearch -m AVC

Õnnestus!

Töötav Django projekt koos front-endiga Nginx ja Gunicorn WSGI-ga on nüüd loodud. Oleme seadistanud Python 3 ja PostgreSQL 10 RHEL 8 Beta hoidlatest. Nüüd saab edasi liikuda ja luua (või lihtsalt juurutada) Django rakendusi või uurida teisi RHEL 8 Beta kergesti kergesti juurutatavaid tööriistu, et automatiseerida seadistamisprotsessi, parandada jõudlust või isegi konteineriseerida see konfiguratsioon.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster