Praktik RHEL 8 Beta: Skapa fungerande webbapplikationer

RHEL 8 Beta erbjuder utvecklare mÄnga nya möjligheter, vars lista kan ta sidor, men att utforska det nya Àr alltid bÀttre i praktiken, sÄ nedan föreslÄr vi att genomföra en praktisk övning i skapandet av en applikationsinfrastruktur baserad pÄ Red Hat Enterprise Linux 8 Beta.

Praktik RHEL 8 Beta: Skapa fungerande webbapplikationer

Vi kommer att anvÀnda Python, ett populÀrt programmeringssprÄk bland utvecklare, tillsammans med Django och PostgreSQL, en ganska vanlig kombination för applikationsutveckling, och konfigurera RHEL 8 Beta för att arbeta med dem. Sedan lÀgger vi till nÄgra (icke-hemliga) ingredienser.

Testmiljön kommer att förĂ€ndras, eftersom det Ă€r intressant att utforska automatiseringsmöjligheter, arbeta med containrar och prova miljöer med flera servrar. För att börja med det nya projektet kan man börja med att skapa en liten, enkel prototyp manuellt – pĂ„ sĂ„ sĂ€tt kan man se vad som exakt ska ske och hur interaktionen genomförs, och sedan gĂ„ vidare till automatisering och skapande av mer komplexa konfigurationer. Idag handlar det om att skapa en sĂ„dan prototyp.

LÄt oss börja med att distribuera en virtuell maskin med RHEL 8 Beta VM. Man kan installera den virtuella maskinen frÄn grunden, eller anvÀnda en gÀstbild KVM som finns tillgÀnglig med en prenumeration pÄ Beta. Vid anvÀndning av gÀstbilden krÀvs det att man konfigurerar en virtuell CD som ska innehÄlla metadata och anvÀndardata för cloud-init. Det behövs inget speciellt för diskstrukturen eller tillgÀngliga paket, vilken konfiguration som helst fungerar.

LÄt oss gÄ igenom hela processen i detalj.

Installation av Django

Med den senaste versionen av Django behövs en virtuell miljö (virtualenv) med Python 3.5 eller nyare. I anteckningarna till Beta kan vi se att Python 3.6 Àr tillgÀngligt, lÄt oss kolla om det verkligen Àr sÄ:

[cloud-user@8beta1 ~]$ python
-bash: python: kommandot hittades inte
[cloud-user@8beta1 ~]$ python3
-bash: python3: kommandot hittades inte

Red Hat anvÀnder aktivt Python som systemverktyg i RHEL, sÄ varför fÄr vi detta resultat?

MÄnga utvecklare som anvÀnder Python funderar fortfarande pÄ att byta frÄn Python 2 till Python 2, medan Python 3 Àr under aktiv utveckling med stÀndiga nya versioner. För att tillmötesgÄ behovet av stabila systemverktyg och samtidigt ge anvÀndarna tillgÄng till olika nya versioner av Python, har system-Python flyttats till ett nytt paket, vilket gör det möjligt att installera bÄde Python 2.7 och 3.6. Mer information om förÀndringarna och skÀlen bakom dem kan hÀmtas frÄn ett inlÀgg i bloggen av Langdon White (Langdon White).

SÄ för att fÄ en fungerande Python, krÀvs det att installera endast tvÄ paket, dÀr python3-pip kommer som en beroende.

sudo yum install python36 python3-virtualenv

Varför man inte bör anvÀnda direkta anrop till modulen som Langdon föreslÄr och inte installera pip3? Med tanke pÄ den kommande automatiseringen, Àr det kÀnt att Ansible krÀver installerad pip, eftersom pip-modulen inte stödjer virtuella miljöer (virtualenvs) med en anpassad pip-exekverbar fil.

Med en fungerande python3-tolk kan vi fortsÀtta installationsprocessen för Django och fÄ ett fungerande system tillsammans med vÄra andra komponenter. Det finns mÄnga varianter av implementationer pÄ nÀtet. HÀr presenteras en version, men anvÀndare kan anvÀnda sina egna processer.

Versionerna av PostgreSQL och Nginx som Àr tillgÀngliga i RHEL 8 som standard ska installeras med Yum.

sudo yum install nginx postgresql-server

För PostgreSQL krÀvs psycopg2, men det behöver vara tillgÀngligt endast i virtualenv-miljön, sÄ vi installera det med pip3 tillsammans med Django och Gunicorn. Men först mÄste vi konfigurera virtualenv.

Det finns alltid mycket diskussioner om rÀtt val av installationsplats för Django-projekt, men nÀr det rÄder osÀkerhet kan man alltid hÀnvisa till standarden Linux Filesystem Hierarchy Standard. Enligt FHS anges att /srv anvÀnds för: "att lagra noder-specifika data som systemet levererar, sÄsom data och skript för webbservrar, data som lagras pÄ FTP-servrar, samt arkiv för versionskontrollsystem (som introducerades i FHS-2.3 Är 2004)".

Detta Àr precis vÄrt fall, sÄ vi lÀgger allt som behövs i /srv, vars Àgare Àr vÄr applikationsanvÀndare (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

InstĂ€llningen av PostgreSQL och Django Ă€r inte komplicerad: vi skapar en databas, skapar en anvĂ€ndare och stĂ€ller in rĂ€ttigheter. Det finns en punkt att tĂ€nka pĂ„ vid den ursprungliga installationen av PostgreSQL – det Ă€r skriptet postgresql-setup, som installeras tillsammans med paketet postgresql-server. Detta skript hjĂ€lper till att utföra grundlĂ€ggande uppgifter relaterade till administration av databas-kluster, sĂ„som initialisering av klustret eller uppgraderingsprocessen. För att konfigurera en ny instans av PostgreSQL pĂ„ RHEL-systemet behöver vi köra kommandot:

sudo /usr/bin/postgresql-setup --initdb

Efter detta kan PostgreSQL startas med systemd, databasen kan skapas och projektet kan konfigureras i Django. Glöm inte att starta om PostgreSQL efter att ha gjort Àndringar i klientautentiseringskonfigurationsfilen (vanligtvis pg_hba.conf) för att konfigurera lagringen av lösenordet för applikationsanvÀndaren. Om du stöter pÄ andra svÄrigheter, se till att instÀllningarna för IPv4 och IPv6 Àr Àndrade i pg_hba.conf-filen.

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

I filen /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

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

Efter att filen settings.py har konfigurerats i projektet och databasinstÀllningarna har stÀllts in, kan utvecklingsservern startas för att se till att allt fungerar. Efter att utvecklingsservern har startats kan det vara bra att skapa en admin-anvÀndare för att testa anslutningen till databasen.

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

WSGI? What?

Utvecklingsservern Àr anvÀndbar för testning, men för att köra applikationen behöver en lÀmplig server och proxy konfigureras för Web Server Gateway Interface (WSGI). Det finns flera vanliga kombinationer, till exempel Apache HTTPD med uWSGI eller Nginx med Gunicorn.

Web Server Gateway Interface uppgift Àr att omdirigera förfrÄgningar frÄn webbservern till Python-webbframeworket. WSGI Àr ett arv frÄn ett förskrÀckligt förflutet, dÄ CGI-mekanismer var pÄ uppgÄng, och idag Àr WSGI faktiskt en standard oavsett vilken webbserver eller Python-ramverk som anvÀnds. Trots dess utbredning finns det dock mÄnga nyanser att beakta nÀr man arbetar med dessa ramverk, och mÄnga valmöjligheter. I detta fall kommer vi att fokusera pÄ att stÀlla in kommunikationen mellan Gunicorn och Nginx via en socket.

Eftersom bÄda dessa komponenter Àr installerade pÄ samma server kommer vi att försöka anvÀnda en UNIX-socket istÀllet för en nÀtverksocket. Eftersom en socket fortfarande behövs för kommunikation, kommer vi att ta ett steg till och konfigurera aktivering av socket för Gunicorn via systemd.

Processen för att skapa socket-aktiverade tjÀnster Àr ganska enkel. Först skapas en enhetsfil som innehÄller direktivet ListenStream, som anger punkten dÀr UNIX-socketen kommer att skapas, och sedan en enhetsfil för tjÀnsten dÀr direktivet Requires kommer att peka pÄ enhetsfilen för socket. I enhetsfilen för tjÀnsten ÄterstÄr det sedan bara att anropa Gunicorn frÄn den virtuella miljön och skapa WSGI-bindningen för UNIX-socketen och Django-applikationen.

HÀr Àr nÄgra exempel pÄ enhetsfiler som kan fungera som grund. Först konfigurerar vi socketen.

[Unit]
Description=Gunicorn WSGI socket

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

[Install]
WantedBy=sockets.target

Nu Àr det dags att konfigurera Gunicorn-demonen.

[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

För Nginx rÀcker det med att skapa proxy-konfigurationsfiler och stÀlla in en katalog för att lagra statiskt innehÄll, om du anvÀnder det. I RHEL ligger Nginx konfigurationsfiler i \/etc\/nginx\/conf.d. Du kan kopiera följande exempel till filen \/etc\/nginx\/conf.d\/default.conf och starta tjÀnsten. Se till att du anger server_name i enlighet med namnet pÄ din vÀrd.

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

Starta Gunicorn- och Nginx-socketen med hjÀlp av systemd, och du kan börja med tester.

Felmeddelande Bad Gateway?

Om du anger en adress i webblÀsaren fÄr du troligtvis felmeddelande 502 Bad Gateway. Detta kan orsakas av felaktigt instÀllda behörigheter för UNIX-socketen eller mer komplexa problem relaterade till Ätkomsthantering i SELinux.

I nginx:s feljournal kan man hitta en rad av detta slag:

2018/12/18 15:38:03 [kritisk] 12734#0: *3 connect() till unix:/run/gunicorn.sock misslyckades (13: Åtkomst nekad) vid anslutning till upstream, klient: 192.168.122.1, server: 8beta1.example.com, begĂ€ran: "GET / HTTP/1.1", upstream: "http://unix:/run/gunicorn.sock:/", vĂ€rd: "8beta1.example.com"

Om vi testar Gunicorn direkt fÄr vi ett tomt svar.

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

LÄt oss undersöka varför detta hÀnder. Om vi öppnar loggen ser vi troligtvis att problemet Àr relaterat till SELinux. Eftersom vi har en daemon som inte har en egen policy, markeras den som init_t. LÄt oss verifiera denna teori i praktiken.

sudo setenforce 0

Allt detta kan orsaka kritik och blodiga tÄrar, men det Àr bara en prototyputredning. Vi stÀnger av kontrollen för att försÀkra oss om att problemet verkligen ligger hÀr, efter vilket vi ÄterstÀller allt till tidigare tillstÄnd.

Genom att uppdatera sidan i webblÀsaren eller genom att starta om vÄr curl-kommandot kan vi se test sidan för Django.

SÄ, nÀr vi har bekrÀftat att allt fungerar och att det inte lÀngre finns nÄgra behörighetsproblem, aktiverar vi SELinux igen.

sudo setenforce 1

HÀr kommer vi inte att diskutera audit2allow och att skapa policies baserat pÄ aviseringar med hjÀlp av sepolgen, eftersom det för nÀrvarande inte finns nÄgon riktig Django-applikation och ingen fullstÀndig karta över vad Gunicorn kan vilja fÄ tillgÄng till, och vad denne Ätkomst ska begrÀnsas till. DÀrför Àr det viktigt att bevara SELinux:s funktion för att skydda systemet, och att samtidigt tillÄta applikationen att köras och skapa loggar i revisionsloggen, sÄ att vi senare kan anvÀnda dem för att skapa en riktig policy.

Angivande av tillÄtna domÀner (permissive domains)

Inte alla har hört talas om tillÄtna domÀner i SELinux, men det Àr ingenting nytt. MÄnga har till och med arbetat med dem utan att veta om det. NÀr en policy skapas baserat pÄ revisionsmeddelanden representerar den skapade policyn en tillÄten domÀn. LÄt oss försöka skapa en enkel tillÄtande policy.

För att skapa en specifik tillÄten domÀn för Gunicorn behövs en viss policy, och motsvarande filer mÄste mÀrkas. Dessutom krÀvs det verktyg för att sammanstÀlla de nya policyerna.

sudo yum install selinux-policy-devel

Mekanismen för tillĂ„tna domĂ€ner Ă€r ett utmĂ€rkt verktyg för att identifiera problem, sĂ€rskilt nĂ€r det gĂ€ller anpassade applikationer eller applikationer som levereras utan redan skapade policyer. I det hĂ€r fallet kommer policy för den tillĂ„tna domĂ€nen för Gunicorn att vara sĂ„ enkel som möjligt – vi deklarerar huvudtypen (gunicorn_t), deklarerar typen som vi ska anvĂ€nda för att mĂ€rka flera körbara filer (gunicorn_exec_t), och sedan konfigurerar vi övergĂ„ngen (transition) för systemet sĂ„ att vi korrekt kan mĂ€rka körda processer. Den sista raden stĂ€ller in policyn som tillĂ„ten som standard vid inlĂ€sning.

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;

Detta policyfil kan kompileras och lÀggas till systemet.

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

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

LÄt oss kontrollera om SELinux blockerar nÄgot annat utöver det som vÄr okÀnda demon försöker komma Ät.

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 tillÄter inte Nginx att skriva data till UNIX-socketen som anvÀnds av Gunicorn. Vanligtvis börjar man Àndra policyer i sÄdana fall, men det finns andra uppgifter som ocksÄ mÄste lösas. Vi kan Àven Àndra domÀninstÀllningarna genom att omvandla den frÄn en begrÀnsningsdomÀn till en tillÄtandedomÀn. Nu flyttar vi httpd_t till tillÄtandedomÀnen. Detta ger Nginx den nödvÀndiga Ätkomsten sÄ att vi kan fortsÀtta vÄrt arbete med felsökning.

sudo semanage permissive -a httpd_t

SÄ snart vi har lyckats hÄlla SELinux-skyddet (det bör egentligen inte lÀmnas med SELinux i begrÀnsningslÀge) och tillÄtandedomÀnerna har laddats, mÄste vi ta reda pÄ vad som exakt behöver mÀrkas som gunicorn_exec_t för att allt ska fungera som det ska. LÄt oss försöka besöka webbplatsen för att se nya meddelanden om begrÀnsad Ätkomst.

sudo ausearch -m AVC -c gunicorn

Det gĂ„r att se mĂ„nga meddelanden som innehĂ„ller ‘comm=«gunicorn»’, som utför olika Ă„tgĂ€rder pĂ„ filer i /srv/djangoapp, sĂ„ det Ă€r uppenbart att detta Ă€r en av kommandona som bör mĂ€rkas.

Men utöver det dyker det upp ett meddelande av denna typ:

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

Om man ser pÄ statusen för gunicorn-tjÀnsten eller kör kommandot ps, sÄ kommer inga aktiva processer att visas. Det verkar som att gunicorn försöker fÄ tillgÄng till Python-tolken i vÄr virtualenv, sannolikt för att köra arbetskript (workers). DÀrför markerar vi nu dessa tvÄ körbara filer och ser om vi kan öppna vÄr test-Django-sida.

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

Det krÀvs en omstart av gunicorn-tjÀnsten för att den nya etiketten ska kunna vÀljas. TjÀnsten kan startas om direkt eller stoppas sÄ att socketen kan starta den nÀr sidan öppnas i webblÀsaren. Kontrollera att processerna fÄtt rÀtt etiketter med hjÀlp av ps.

ps -efZ | grep gunicorn

Glöm inte att skapa en korrekt SELinux-policy senare!

Om man nu ser pÄ AVC-meddelandena, sÄ finns det senaste meddelandet som innehÄller permissive=1 för allt som rör applikationen och permissive=0 för hela systemet. Om man förstÄr vilken Ätkomst den verkliga applikationen behöver kan man snabbare hitta den bÀsta lösningen pÄ sÄdana problem. Men tills dess Àr det bÀttre att systemet Àr skyddat, och att fÄ en tydlig och anvÀndbar granskningsrapport för Django-projektet.

sudo ausearch -m AVC

Det lyckades!

Ett fungerande Django-projekt med frontend pÄ Nginx och Gunicorn WSGI har skapats. Vi har konfigurerat Python 3 och PostgreSQL 10 frÄn RHEL 8 Beta-repositorierna. Nu kan vi gÄ vidare och skapa (eller bara distribuera) Django-applikationer eller utforska andra verktyg tillgÀngliga i RHEL 8 Beta för att automatisera installationsprocessen, förbÀttra prestanda eller till och med containerisera denna konfiguration.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster