Praktični tečaj RHEL 8 Beta: Ustvarjanje delujočih spletnih aplikacij

RHEL 8 Beta provides developers with many new opportunities, the list of which could fill pages. However, it's always better to learn something new through practice, so below we offer a practical course on creating application infrastructure based on Red Hat Enterprise Linux 8 Beta.

Praktični tečaj RHEL 8 Beta: Ustvarjanje delujočih spletnih aplikacij

We will base our project on Python, a popular programming language among developers, along with Django and PostgreSQL, a common stack for application development, and configure RHEL 8 Beta to work with them. Later, we will add a couple more (non-secret) ingredients.

The testing environment will change, as it's interesting to explore automation possibilities, work with containers, and try out multi-server environments. To get started with the new project, you can create a small simple prototype manually — this way you can see exactly what should happen and how the interaction occurs, and then move on to automation and creating more complex configurations. Today, we will discuss how to create such a prototype.

Let’s begin by deploying the RHEL 8 Beta VM image. You can install the virtual machine from scratch or use the KVM guest image available with the Beta subscription. When using the guest image, you will need to configure a virtual CD that will contain the metadata and user data for cloud-init. There’s no need to make any special arrangements with the disk structure or available packages; any configuration will do.

Let's take a closer look at the entire process.

Installing Django

With the latest version of Django, a virtual environment (virtualenv) with Python 3.5 or later is required. In the Beta notes, it is stated that Python 3.6 is available, so let's check if that's the case:

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

Red Hat actively uses Python as a system tool in RHEL, so why is this result occurring?

Mnogi programeri koji koriste Python još uvek razmišljaju o prelasku sa Python 2 na Python 2, dok se sam Python 3 aktivno razvija, a nove verzije se neprekidno pojavljuju. Stoga je, kako bi se zadovoljila potreba za stabilnim sistemskim alatima, a istovremeno korisnicima omogućio pristup različitim novim verzijama Pythona, sistemski Python prebačen u novi paket i omogućena je instalacija i Python 2.7 i 3.6. Dodatne informacije o promenama i razlozima za ovo mogu se pronaći u objavi na blogu Lengdona Vajta (Langdon White).

Dakle, da biste dobili funkcionalan Python, potrebno je instalirati samo dva paketa, pri čemu se python3-pip povlači kao zavisnost.

sudo yum install python36 python3-virtualenv

Zašto ne treba koristiti direktne pozive modulu, kao što sugeriše Lengdon, i ne instalirati pip3? Imajući u vidu predstojeću automatizaciju, poznato je da će za rad Ansible-a biti potreban instalirani pip, jer pip modul ne podržava virtuelna okruženja (virtualenvs) sa prilagođenim izvršnim fajlom pip.

Imajući funkcionalni interpretator python3, možemo nastaviti proces instalacije Django-a i dobiti funkcionalan sistem zajedno sa našim ostalim komponentama. Na mreži postoji mnogo opcija realizacije. Ovdje je prikazana jedna verzija, ali korisnici mogu koristiti svoje vlastite procese.

Verzije PostgreSQL-a i Nginx-a dostupne u RHEL 8 po defaultu biće instalirane pomoću Yum.

sudo yum install nginx postgresql-server

Za PostgreSQL će biti potreban psycopg2, ali treba da bude dostupan samo u virtuelnom okruženju, tako da ćemo ga instalirati pomoću pip3 zajedno sa Django-om i Gunicorn-om. Ali prvo treba da postavimo virtualenv.

O pitanju pravog izbora mesta za instalaciju projekata Django uvek postoji mnogo rasprava, ali kada se pojave sumnje, uvek se može pozvati na standard Linux Filesystem Hierarchy Standard. Konkretno, u FHS-u se navodi da /srv služi za: „čuvanje podataka specifičnih za određeni čvor – podataka koje izbacuje sistem, kao što su podaci i skripte veb servera, podaci koji se čuvaju na FTP serverima, kao i repozitorijumi sistema za kontrolu verzija (koji su se pojavili u FHS-2.3 2004. godine)“.

To je naš slučaj, zato stavljamo sve potrebno u /srv, čiji je vlasnik naš korisnik aplikacije (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

Podešavanje PostgreSQL i Django ne predstavlja poteškoće: kreiramo bazu podataka, kreiramo korisnika, podešavamo dozvole. Postoji jedan momenat koji treba zapamtiti prilikom početne instalacije PostgreSQL – to je skripta postgresql-setup, koja se instalira zajedno sa paketom postgresql-server. Ova skripta pomaže u osnovnim zadacima vezanim za administraciju klastera baza podataka, kao što su inicijalizacija klastera ili proces ažuriranja. Za podešavanje novog primerka PostgreSQL na sistemu RHEL potrebno je izvršiti komandu:

sudo /usr/bin/postgresql-setup -initdb

Nakon toga, možete pokrenuti PostgreSQL pomoću systemd, kreirati bazu podataka i podesiti projekat u Django. Ne zaboravite da ponovo pokrenete PostgreSQL nakon što izvršite izmene u datoteci konfiguracije autentikacije klijenta (obično pg_hba.conf) za podešavanje skladištenja lozinke za korisnika aplikacije. Ako naiđete na druge poteškoće, uverite se da su podešene vrednosti za IPv4 i IPv6 u datoteci 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

U datoteci /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

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

Nakon konfiguracije datoteke settings.py u projektu i podešavanja konfiguracije baze podataka, možete pokrenuti razvojni server kako biste se uverili da sve funkcioniše. Nakon pokretanja razvojnog servera, dobro je da kreirate admin korisnika kako biste testirali konekciju sa bazom podataka.

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

WSGI? Šta?

Razvojni server je koristan za testiranje, ali za pokretanje aplikacije potrebno je podesiti odgovarajući server i proxy za Web Server Gateway Interface (WSGI). Postoji nekoliko uobičajenih kombinacija, kao što su Apache HTTPD sa uWSGI ili Nginx sa Gunicorn.

Zadatak Web Server Gateway Interface je preusmeravanje zahteva od spletnega strežnika do spletnega okvira Python. WSGI je nekakšno dediščina grozne preteklosti, ko so bili v uporabi mehanizmi CGI, in danes je WSGI dejansko standard, ne glede na uporabljeni spletni strežnik ali Python okvir. Kljub njegovi široki uporabi pa obstaja še vedno veliko posebnosti pri delu s temi okviri in številne možnosti izbire. V tem primeru bomo poskušali vzpostaviti interakcijo med Gunicorn in Nginx preko soketa.

Ker sta oba ta komponenta nameščena na istem strežniku, bomo poskusili uporabiti namesto omrežnega soketa UNIX soket. Ker potrebujemo soket za komunikacijo, bomo poskusili narediti še en korak in nastaviti aktivacijo soketa za Gunicorn preko systemd.

Postopek ustvarjanja storitev, ki jih aktivirajo soketi (socket activated services), je precej preprost. Najprej se ustvari enota datoteka, ki vsebuje direktivo ListenStream, ki kaže na točko, kjer bo ustvarjen UNIX soket, nato pa enota datoteka za storitve, v kateri bo direktiva Requires kazala na enoto datoteko soketa. Nato bo v enoti datoteki storitve preostalo samo še poklicati Gunicorn iz virtualnega okolja in ustvariti WSGI vezavo za UNIX soket in aplikacijo Django.

Tukaj je nekaj primerov enotnih datotek, ki jih lahko vzamete kot osnovo. Najprej nastavimo soket.

[Enota]
Opis=Gunicorn WSGI soket

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

[Nastavi]
ŽeljenoBy=sockets.target

Zdaj je potrebno nastaviti demon Gunicorn.

[Enota]
Opis=Gunicorn demon
Zahteva=gunicorn.socket
Po=network.target

[Storitev]
Uporabnik=cloud-user
Skupina=cloud-user
DelovnaMapa=\/srv\/djangoapp

ExecStart=\/srv\/djangoapp\/django\/bin\/gunicorn 
         —access-logfile - 
         —workers 3 
         —bind unix:gunicorn.sock djangoapp.wsgi

[Nastavi]
ŽeljenoBy=multi-user.target

Za Nginx je dovolj, da preprosto ustvarite datoteke za konfiguracijo proxy in nastavite mapo za shranjevanje statičnih vsebin, če jo uporabljate. V RHEL se datoteke za konfiguracijo Nginx nahajajo v /etc/nginx/conf.d. Lahko kopirate naslednji primer v datoteko /etc/nginx/conf.d/default.conf in zaženete storitev. Poskrbite, da ste navedli server_name v skladu z imenom vašega gostišča.

strežnik {
   poslušaj 80;
   server_name 8beta1.example.com;

   lokacija = \/favicon.ico { access_log off; log_not_found off; }
   lokacija \/static\/ {
       root \/srv\/djangoapp;
   }

   lokacija \/ {
       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;
   }
}

Zaženite soket Gunicorn in Nginx s pomočjo systemd in lahko začnete s testiranjem.

Napaka Bad Gateway?

Če v brskalnik vnesete naslov, boste verjetno naleteli na napako 502 Bad Gateway. To lahko povzroči nepravilno nastavljena dovoljenja za UNIX soket, ali pa so vzroki bolj kompleksni in povezani z upravljanjem dostopa v SELinuxu.

V dnevniku napak nginx-a lahko najdete vrstico podobnega tipa:

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"

Če testiramo Gunicorn neposredno, bomo dobili prazno odgovor.

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

Poglejmo, zakaj se to dogaja. Če odprete dnevnik, boste verjetno videli, da je težava povezana s SELinuxom. Ker imamo zagnan demon, ki nima svoje politike, je označen kot init_t. Preverimo to teorijo v praksi.

sudo setenforce 0

Vse to lahko povzroči kritiko in krvave solze, a to je le odpravljanje napak prototipa. Funkcijo preverjanja izklopimo le, da se prepričamo, da je težava res v tem, potem pa bomo vse vrnili na svoje mesto.

Po osvežitvi strani v brskalniku ali ponovnem zagonu naše ekipe curl lahko vidimo testno stran Django.

Torej, ko smo se prepričali, da vse deluje in da ni več težav z dovoljenji, ponovno vključimo SELinux.

sudo setenforce 1

Tu ne bomo govorili o audit2allow in o ustvarjanju politik na podlagi obvestil z uporabo sepolgen, saj trenutno ni pravega Django aplikacije, torej ni popolne karte tistega, do česar bi želel priti Gunicorn in česar dostop bi je treba prepovedati. Zato je treba ohraniti delovanje SELinuxa za zaščito sistema in hkrati omogočiti aplikaciji, da se zažene in pušča sporočila v dnevniku, da bo mogoče na njihovi osnovi nato ustvariti pravo politiko.

Določitev dovoljenih domen (permissive domains)

O dovoljenih domenah v SELinuxu ni slišalo veliko ljudi, vendar ni nič novega. Mnogi so jih celo uporabljali, ne da bi se tega zavedali. Ko se politika ustvari na podlagi obvestil za audit, ta politika predstavlja dovoljeno domeno. Poskusimo ustvariti najpreprostejšo dovolilno politiko.

Da bi ustvarili specifično dovoljeno domeno za Gunicorn, potrebujete neko politiko in označiti morate ustrezne datoteke. Poleg tega so potrebna orodja za sestavljanje novih politik.

sudo yum install selinux-policy-devel

Mehanizem dovoljenih domen je odlično orodje za odkrivanje težav, zlasti ko gre za prilagojeno aplikacijo ali aplikacije, ki se dostavljajo brez že ustvarjenih politik. V tem primeru bo politika dovoljene domene za Gunicorn čim enostavnejša – razglasimo glavni tip (gunicorn_t), razglasimo tip, ki ga bomo uporabili za označevanje več izvršnih datotek (gunicorn_exec_t), nato pa nastavimo prehod (transition) za system, da pravilno označimo zagnane procese. Zadnja vrstica nastavi politiko kot privzeto dovoljeno ob njenem nalaganju.

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;

Ta dokument politike lahko sestavite in dodate v sistem.

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

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

Poglejmo, ali SELinux še kaj blokira, poleg tistega, kar obravnava naš neznan demon.

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 ne omogoča Nginxu zapisovanje podatkov v UNIX soket, ki ga uporablja Gunicorn. Običajno v takih primerih začnejo spreminjati politike, vendar je pred nami še več nalog. Prav tako lahko spremenite nastavitve domene, da jo spremenite iz omejevanja v dovoljeno domeno. Zdaj bomo prenesli httpd_t v dovoljeno domeno. To bo Nginxu omogočilo želen dostop, in lahko nadaljujemo z odpravljanjem napak.

sudo semanage permissive -a httpd_t

Torej, ko je bilo mogoče ohraniti zaščito SELinux (dejansko ne bi smeli pustiti projekta v načinu omejevanja) in dovoljene domene se naložijo, je treba ugotoviti, kaj točno je treba označiti kot gunicorn_exec_t, da bo vse znova delovalo kot treba. Poskusimo dostopiti do spletne strani, da vidimo nova sporočila o omejitvah dostopa.

sudo ausearch -m AVC -c gunicorn

Vidite lahko veliko sporočil, ki vsebujejo ‘comm=«gunicorn»’, ki izvajajo različne akcije nad datotekami v /srv/djangoapp, zato je očitno, da gre za eno izmed ukazov, ki jo je treba označiti.

Poleg tega se prikaže sporočilo takšne oblike:

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

Če pogledate status storitve gunicorn ali izvedete ukaz ps, ne boste opazili nobenih zagnanih procesov. Zdi se, da gunicorn poskuša dostopati do interpreterja Python v našem okolju virtualenv, morda za zagon delovnih skript (workers). Zato bomo sedaj označili ti dve izvršljivi datoteki in preverili, ali lahko odpreti našo testno stran Django.

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

Znova bo treba zagnati storitev gunicorn, da se bo izbrala nova oznaka. Lahko jo znova zaženete takoj ali zaustavite storitev in pustite, da jo ustrežni socket zažene ob odprtju strani v brskalniku. Prepričajte se, da so procesi prejeli prave oznake, s pomočjo ukaza ps.

ps -efZ | grep gunicorn

Ne pozabite nato ustvariti pravilne politike SELinux!

Če sedaj pogledate sporočila AVC, zadnje sporočilo vsebuje permissive=1 za vse, kar se nanaša na aplikacijo, in permissive=0 za celoten preostali sistem. Če razumete, kateri dostop je potreben dejanski aplikaciji, lahko hitreje najdete optimalen način reševanja takšnih težav. A do takrat je bolje, da je sistem zaščiten in da pridobite razumljiv in uporaben audit za projekt Django.

sudo ausearch -m AVC

Uspešno!

Pojavil se je delujoč projekt Django s frontendom na Nginx in Gunicorn WSGI. Nastavili smo Python 3 in PostgreSQL 10 iz repozitorijev RHEL 8 Beta. Sedaj se lahko premaknemo naprej in razvijamo (ali zgolj namestimo) Django aplikacije ali raziskujemo druga razpoložljiva orodja v RHEL 8 Beta za avtomatizacijo postopka nastavitve, povečanje učinkovitosti ali celo kontejnerizacijo te konfiguracije.

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster