RHEL 8 Beta erbjuder utvecklare mÄnga nya möjligheter, listan kan ta sidor, men att lÀra sig nya saker Àr alltid bÀttre i praktiken, dÀrför föreslÄr vi nedan att genomföra en praktisk övning i skapandet av applikationsinfrastruktur byggd pÄ Red Hat Enterprise Linux 8 Beta.

Vi utgÄr frÄn Python, ett populÀrt programmeringssprÄk bland utvecklare, tillsammans med Django och PostgreSQL, en relativt vanlig kombination för att skapa applikationer, och konfigurerar RHEL 8 Beta för att arbeta med dessa. Sedan lÀgger vi till nÄgra (icke-hemliga) ingredienser.
Testmiljön kommer att variera, eftersom det Ă€r intressant att utforska möjligheterna till automatisering, jobba med containrar och pröva miljöer med flera servrar. För att börja med det nya projektet kan man först skapa en liten och enkel prototyp manuellt â pĂ„ sĂ„ sĂ€tt kan man se vad som faktiskt ska hĂ€nda och hur interaktionen gĂ„r till, och sedan gĂ„ vidare till automatisering och mer komplexa konfigurationer. Idag handlar det om att skapa en sĂ„dan prototyp.
LÄt oss börja med att distribuera en RHEL 8 Beta VM-bild. Man kan installera en virtuell maskin frÄn grunden eller anvÀnda en gÀstbild KVM som finns tillgÀnglig med en Beta-prenumeration. NÀr man anvÀnder en gÀstbild behöver man konfigurera en virtuell CD som innehÄller metadata och anvÀndardata för cloud-init. Det behövs inget speciellt med diskstrukturen eller de tillgÀngliga paketen, vilken konfiguration som helst fungerar.
LÄt oss se pÄ hela processen mer i detalj.
Installation av Django
Med den senaste versionen av Django krÀvs en virtuell miljö (virtualenv) med Python 3.5 eller en senare version. I anmÀrkningarna till Beta kan man se att Python 3.6 Àr tillgÀnglig, lÄt oss kontrollera om det verkligen Àr sÄ:
[cloud-user@8beta1 ~]$ python
-bash: python: command not found
[cloud-user@8beta1 ~]$ python3
-bash: python3: command not found
Red Hat anvÀnder aktivt Python som systemverktyg i RHEL, sÄ varför fÄr vi ett sÄdant resultat?
MÄnga utvecklare som anvÀnder Python funderar fortfarande pÄ att övergÄ frÄn Python 2 till Python 2, medan Python 3 Àr under aktiv utveckling och stÀndigt nya versioner slÀpps. För att möta behovet av stabila systemverktyg samt erbjuda anvÀndare tillgÄng till olika nya versioner av Python, har standard-Python förflyttats till ett nytt paket för att möjliggöra installation av bÄde Python 2.7 och 3.6. Mer information om förÀndringarna och varför detta har gjorts kan hittas i publikationen pÄ (Langdon White).
SÄ för att fÄ en fungerande Python behöver man installera endast tvÄ paket, dÀr python3-pip kommer som ett beroende.
sudo yum install python36 python3-virtualenv
Varför bör man inte anvÀnda direkta anrop till modulen som föreslÄs av Langdon, 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 anpassade pip-exekverbara filer.
Med en fungerande python3-tolk kan man fortsÀtta installationsprocessen för Django och fÄ ett fungerande system tillsammans med vÄra andra komponenter. Det finns mÄnga olika implementeringsalternativ tillgÀngliga pÄ nÀtet. HÀr presenteras en version, men anvÀndarna kan anvÀnda sina egna processer.
Versionerna av PostgreSQL och Nginx som Àr tillgÀngliga i RHEL 8 som standard kommer vi att installera 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 kommer att installera det med pip3 tillsammans med Django och Gunicorn. Men först mÄste vi konfigurera virtualenv.
Det rĂ„der alltid mycket diskussioner om valet av installationsplats för Django-projekt, men nĂ€r det uppstĂ„r tvivel kan man alltid hĂ€nvisa till standarden för Linux Filesystem Hierarchy Standard. Specifikt anges det i FHS att /srv anvĂ€nds för: "lagring av data som Ă€r specifika för en viss nod â data som systemet levererar, sĂ„som data och skript för webbservrar, data som lagras pĂ„ FTP-servrar, samt förrĂ„d för versionskontrollsystem (som introducerades i FHS-2.3 2004)".
Det hÀr Àr precis vÄr situation, sÄ vi lÀgger allt nödvÀndigt 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 konfigurerar rĂ€ttigheter. Det finns en sak att komma ihĂ„g vid den första installationen av PostgreSQL â skriptet postgresql-setup, som installeras med paketet postgresql-server. Detta skript hjĂ€lper till att utföra grundlĂ€ggande uppgifter relaterade till administrationen av databaskluster, till exempel initialisering av klustret eller uppdateringsprocessen. För att konfigurera en ny PostgreSQL-instans pĂ„ RHEL-systemet behöver vi köra kommandot:
sudo /usr/bin/postgresql-setup -initdb
Efter detta kan vi starta PostgreSQL med systemd, skapa databasen och konfigurera projektet i Django. Glöm inte att starta om PostgreSQL efter att ha gjort Àndringar i klientauktoriseringskonfigurationsfilen (vanligtvis pg_hba.conf) för att konfigurera lösenordshanteringen för applikationsanvÀndaren. Om du stöter pÄ andra problem, se till att instÀllningarna för IPv4 och IPv6 har Àndrats i filen 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
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 ha konfigurerat filen settings.py i projektet och databaskonfigurationen kan vi starta utvecklingsservern för att kontrollera att allt fungerar. Efter att ha startat utvecklingsservern Àr det 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? Vad?
Utvecklingsservern Àr anvÀndbar för testning, men för att köra applikationen mÄste vi konfigurera en lÀmplig server och proxy för Web Server Gateway Interface (WSGI). Det finns flera vanliga kopplingar, till exempel Apache HTTPD med uWSGI eller Nginx med Gunicorn.
Web Server Gateway Interfaces uppgift Àr att vidarebefordra förfrÄgningar frÄn webbservern till Python-webbramverket. WSGI Àr ett arv frÄn ett fruktansvÀrt förflutet, nÀr CGI-mekanismer var i bruk, och idag Àr WSGI i praktiken standard, oavsett vilken webbserver eller Python-ramverk som anvÀnds. Men trots dess utbredda anvÀndning finns det fortfarande mÄnga nyanser vid arbete med dessa ramverk och mÄnga valmöjligheter. I detta fall kommer vi att försöka fÄ Gunicorn och Nginx att samverka 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Àtverks-socket. Eftersom vi i vilket fall som helst behöver en socket för kommunikation, kommer vi att ta ett steg ytterligare och konfigurera socket-aktivering för Gunicorn via systemd.
Processt för att skapa tjÀnster som aktiveras av sockets Àr ganska enkelt. Först skapas en enhetsfil som innehÄller direktivet ListenStream, som anger punkten dÀr en UNIX-socket kommer att skapas, och sedan en enhetsfil för tjÀnsten dÀr direktivet Requires pekar pÄ socket-enhetsfilen. I enhetsfilen för tjÀnsten ÄterstÄr endast att anropa Gunicorn frÄn den virtuella miljön och skapa en WSGI-bindning för UNIX-socket och Django-applikationen.
HÀr Àr nÄgra exempel pÄ enhetsfiler som kan anvÀndas som grund. Först konfigurerar vi socketen.
[Unit]
Description=Gunicorn WSGI socket
[Socket]
ListenStream=\/run\/gunicorn.sock
[Install]
WantedBy=sockets.target
Nu mÄste vi 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 det tillrÀckligt att skapa proxykonfigurationsfiler och konfigurera en katalog för att lagra statiskt innehÄll om du anvÀnder en sÄdan. I RHEL ligger Nginx konfigurationsfiler pÄ \/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 angav server_name i enlighet med ditt vÀrdnamn.
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-socketen och Nginx med hjÀlp av systemd, och sÄ kan du börja med testningen.
Felmeddelande Bad Gateway?
Om du skriver in adressen i webblÀsaren, fÄr du troligen felmeddelandet 502 Bad Gateway. Detta kan orsakas av felaktigt konfigurerade behörigheter för UNIX-socketen eller mer komplexa problem relaterade till Ätkomsthantering i SELinux.
I nginx:s felförloppslogg kan man stöta pÄ en rad av följande slag:
2018/12/18 15:38:03 [kritisk] 12734#0: *3 connect() till unix:/run/gunicorn.sock misslyckades (13: Behörighet nekad) vid anslutning till upstream, klient: 192.168.122.1, server: 8beta1.example.com, förfrÄgan: "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 igÄng som inte har en egen policy skapad, markerar den sig sjÀlv som init_t. LÄt oss verifiera denna teori praktiskt.
sudo setenforce 0
Allt detta kan leda till kritik och blodiga tÄrar, men det Àr bara en debug-typ prototyp. Vi stÀnger av kontrollen bara för att sÀkerstÀlla att problemet verkligen ligger hÀr, och efter det ÄterstÀller vi allt till sin ordning igen.
Genom att uppdatera sidan i webblÀsaren eller starta om vÄr curl-kommando kan vi se test sidan för Django.
SÄ, nÀr vi har sÀkerstÀllt att allt fungerar och inga fler behörighetsproblem kvarstÄr, sÀtter vi Äter SELinux pÄ.
sudo setenforce 1
HÀr kommer det inte att diskuteras audit2allow och skapandet av policys baserat pÄ varningar med hjÀlp av sepolgen, eftersom det för tillfÀllet inte finns nÄgon verklig Django-applikation, sÄ det finns ingen fullstÀndig karta över vad Gunicorn kan vilja fÄ tillgÄng till, och vad det bör förhindras att fÄ tillgÄng till. DÀrför Àr det nödvÀndigt att bevara SELinux:s funktionalitet för att skydda systemet, samtidigt som applikationen fÄr starta och lÀmna meddelanden i auditloggen, sÄ att riktiga policies kan skapas baserat pÄ dem.
Angivna tillÄtna domÀner (permissive domains)
Inte alla har hört talas om tillÄtna domÀner i SELinux, men de Àr inget nytt. MÄnga har till och med arbetat med dem utan att inse det. NÀr en policy skapas baserat pÄ auditmeddelanden, representerar den skapade policyn en tillÄten domÀn. LÄt oss försöka skapa en enklast möjlig tillÄtande policy.
För att skapa en specifik tillÄten domÀn för Gunicorn krÀvs en viss policy, samt att mÀrka de relevanta filerna. Dessutom behövs verktyg för att sammanstÀlla nya policies.
sudo yum install selinux-policy-devel
Systemet 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 policies. I det hĂ€r fallet kommer policyn 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 kommer att anvĂ€nda för att mĂ€rka flera körbara filer (gunicorn_exec_t) och konfigurerar sedan övergĂ„ngen (transition) för systemet sĂ„ att vi korrekt kan mĂ€rka de körande processerna. Den sista raden stĂ€ller in policyn som tillĂ„ten som standard vid inladdning.
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 i 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 daemon försöker fÄ Ätkomst till.
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 hindrar Nginx frÄn att skriva data till den UNIX-socket som anvÀnds av Gunicorn. Vanligtvis börjar man Àndra policies i sÄdana fall, men det finns andra problem som mÄste lösas först. Vi kan dessutom förÀndra domÀninstÀllningarna, vilket gör det möjligt att omvandla den frÄn en begrÀnsningsdomÀn till en tillÄtelsedomÀn. Nu flyttar vi httpd_t till tillÄtelsedomÀnen. Detta ger Nginx nödvÀndig Ätkomst, och vi kan fortsÀtta vÄrt arbete med felsökning.
sudo semanage permissive -a httpd_t
SÄ, nÀr vi lyckades bibehÄlla skyddet av SELinux (det Àr faktiskt inte bra att lÀmna projektet i begrÀnsningslÀge) och tillÄtelsedomÀnerna laddas, behöver vi ta reda pÄ vad som exakt behöver mÀrkas som gunicorn_exec_t sÄ att allt börjar fungera som det ska. LÄt oss försöka nÄ 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 det hÀr Àr en av kommandona som bör mÀrkas.
Men dessutom dyker ett meddelande av följande typ upp:
type=AVC msg=audit(1545320700.070:1542): avc: denied { execute } för 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 kollar statusen för gunicorn-tjÀnsten eller kör kommandot ps, kommer inga aktiva processer att visas. Det verkar som att gunicorn försöker nÄ Python-tolken i vÄr virtualenv, troligtvis för att starta arbetsskript (workers). DÀrför kommer vi nu att mÀrka dessa tvÄ körbara filer och kontrollera om vi kan öppna vÄr test-sida i Django.
chcon -t gunicorn_exec_t /srv/djangoapp/django/bin/gunicorn /srv/djangoapp/django/bin/python3.6
Det kommer att krÀvas att gunicorn-tjÀnsten startas om för att den nya etiketten ska kunna vÀljas. Den kan startas om direkt eller stoppas och lÄtas starta om den behöver det nÀr sidan öppnas i webblÀsaren. Se till att processerna fÄtt de nödvÀndiga etiketterna genom att anvÀnda ps.
ps -efZ | grep gunicorn
Glöm inte att sedan skapa en korrekt SELinux-policy!
Om man ser AVC-meddelandena nu ser det sista meddelandet att permissive=1 för allt som rör tillÀmpningen och permissive=0 för resten av systemet. Om man förstÄr vilken typ av Ätkomst den verkliga applikationen behöver kan man snabbare hitta den optimala lösningen för liknande problem. Men tills dess Àr det bÀttre att systemet Àr skyddat, och att man fÄr en begriplig och anvÀndbar revision av Django-projektet.
sudo ausearch -m AVC
Det fungerade!
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-repositories. Nu kan vi gÄ vidare och skapa (eller helt enkelt distribuera) Django-applikationer eller utforska andra tillgÀngliga verktyg i RHEL 8 Beta för att automatisera installationsprocessen, öka prestanda eller till och med containerisera denna konfiguration.
KĂ€lla: habr.com
