Workshop RHEL 8 Beta: Bygga fungerande webbapplikationer

RHEL 8 Beta erbjuder utvecklare många nya funktioner, vars lista kan ta sidor, men att lära sig nya saker är alltid bättre i praktiken, så nedan erbjuder vi en workshop om att faktiskt skapa en applikationsinfrastruktur baserad på Red Hat Enterprise Linux 8 Beta.

Workshop RHEL 8 Beta: Bygga fungerande webbapplikationer

Låt oss ta Python, ett populärt programmeringsspråk bland utvecklare, som grund, en kombination av Django och PostgreSQL, en ganska vanlig kombination för att skapa applikationer, och konfigurera RHEL 8 Beta för att fungera med dem. Sedan lägger vi till ytterligare ett par (oklassificerade) ingredienser.

Testmiljön kommer att förändras, eftersom det är intressant att utforska möjligheterna med automatisering, att arbeta med containrar och prova miljöer med flera servrar. För att komma igång med ett nytt projekt kan du börja med att skapa en liten, enkel prototyp för hand så att du kan se exakt vad som behöver hända och hur det samverkar, och sedan gå vidare med att automatisera och skapa mer komplexa konfigurationer. Idag talar vi om skapandet av en sådan prototyp.

Låt oss börja med att distribuera RHEL 8 Beta VM-avbildningen. Du kan installera en virtuell maskin från början eller använda KVM-gästavbildningen som finns tillgänglig med din Beta-prenumeration. När du använder en gästavbildning måste du konfigurera en virtuell CD som innehåller metadata och användardata för molninitiering (cloud-init). Du behöver inte göra något speciellt med diskstrukturen eller tillgängliga paket, vilken konfiguration som helst duger.

Låt oss ta en närmare titt på hela processen.

Installerar Django

Med den senaste versionen av Django behöver du en virtuell miljö (virtualenv) med Python 3.5 eller senare. I betaanteckningarna kan du se att Python 3.6 är tillgänglig, låt oss kontrollera om detta verkligen är fallet:

[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 en systemverktygslåda i RHEL, så varför blir detta resultat?

Faktum är att många Python-utvecklare fortfarande överväger övergången från Python 2 till Python 2, medan själva Python 3 är under aktiv utveckling, och fler och fler nya versioner dyker upp hela tiden. Därför, för att möta behovet av stabila systemverktyg och samtidigt erbjuda användarna tillgång till olika nya versioner av Python, flyttades systemet Python till ett nytt paket och gav möjligheten att installera både Python 2.7 och 3.6. Mer information om ändringarna och varför de gjordes finns i publikationen i Langdon Whites blogg (Langdon White).

Så för att få fungerande Python behöver du bara installera två paket, med python3-pip inkluderat som ett beroende.

sudo yum install python36 python3-virtualenv

Varför inte använda direkta modulanrop som Langdon föreslår och installera pip3? Med tanke på den kommande automatiseringen är det känt att Ansible kommer att kräva pip installerat för att köras, eftersom pip-modulen inte stöder virtualenvs med en anpassad pip-körbar.

Med en fungerande python3-tolk till ditt förfogande kan du fortsätta med Django-installationsprocessen och ha ett fungerande system tillsammans med våra andra komponenter. Det finns många implementeringsalternativ tillgängliga på Internet. Det finns en version som presenteras här, men användare kan använda sina egna processer.

Vi kommer att installera PostgreSQL- och Nginx-versionerna som är tillgängliga i RHEL 8 som standard med Yum.

sudo yum install nginx postgresql-server

PostgreSQL kommer att kräva psycopg2, men det behöver bara vara tillgängligt i en virtualenv-miljö, så vi kommer att installera det med pip3 tillsammans med Django och Gunicorn. Men först måste vi ställa in virtualenv.

Det finns alltid en hel del debatt om ämnet att välja rätt plats för att installera Django-projekt, men när du är osäker kan du alltid vända dig till Linux Filesystem Hierarchy Standard. Specifikt säger FHS att /srv används för att: "lagra värdspecifik data—data som systemet producerar, såsom webbserverdata och skript, data lagrade på FTP-servrar och styrsystemlager." versioner (visas i FHS -2.3 år 2004)."

Detta är precis vårt fall, så vi lägger allt vi behöver i /srv, som ägs av vår applikationsanvändare (molnanvändare).

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

Det är enkelt att ställa in PostgreSQL och Django: skapa en databas, skapa en användare, konfigurera behörigheter. En sak att tänka på när du initialt installerar PostgreSQL är postgresql-setup-skriptet som installeras med postgresql-server-paketet. Det här skriptet hjälper dig att utföra grundläggande uppgifter associerade med databasklusteradministration, såsom klusterinitiering eller uppgraderingsprocessen. För att konfigurera en ny PostgreSQL-instans på ett RHEL-system måste vi köra kommandot:

sudo /usr/bin/postgresql-setup -initdb

Du kan sedan starta PostgreSQL med systemd, skapa en databas och ställa in ett projekt i Django. Kom ihåg att starta om PostgreSQL efter att ha gjort ändringar i klientautentiseringskonfigurationsfilen (vanligtvis pg_hba.conf) för att konfigurera lösenordslagring för applikationsanvändaren. Om du stöter på andra problem, se till att ändra IPv4- och IPv6-inställningarna 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 ställt in databaskonfigurationen kan du starta utvecklingsservern för att se till att allt fungerar. Efter att ha startat utvecklingsservern är det en bra idé att skapa en administratörsanvändare för att testa anslutningen till databasen.

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

WSGI? Wai?

Utvecklingsservern är användbar för testning, men för att köra applikationen måste du konfigurera lämplig server och proxy för Web Server Gateway Interface (WSGI). Det finns flera vanliga kombinationer, till exempel Apache HTTPD med uWSGI eller Nginx med Gunicorn.

Jobbet för Web Server Gateway Interface är att vidarebefordra förfrågningar från webbservern till Pythons webbramverk. WSGI är en kvarleva från det fruktansvärda förflutna när CGI-motorer fanns, och idag är WSGI de facto standarden, oavsett vilken webbserver eller Python-ramverk som används. Men trots dess utbredda användning finns det fortfarande många nyanser när man arbetar med dessa ramverk, och många val. I det här fallet kommer vi att försöka etablera interaktion mellan Gunicorn och Nginx via en socket.

Eftersom båda dessa komponenter är installerade på samma server, låt oss försöka använda ett UNIX-uttag istället för ett nätverksuttag. Eftersom kommunikation kräver en socket i alla fall, låt oss försöka ta ett steg till och konfigurera socketaktivering för Gunicorn via systemd.

Processen att skapa socketaktiverade tjänster är ganska enkel. Först skapas en enhetsfil som innehåller ett ListenStream-direktiv som pekar på punkten där UNIX-socket kommer att skapas, sedan en enhetsfil för tjänsten där Requires-direktivet pekar på socket-enhetsfilen. Sedan, i serviceenhetsfilen, återstår bara 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 du kan använda som grund. Först satte vi upp uttaget.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Nu måste du 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 en enkel fråga att skapa proxykonfigurationsfiler och ställa in en katalog för att lagra statiskt innehåll om du använder en. I RHEL finns 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 ställa in servernamnet så att det matchar 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-uttaget och Nginx med systemd och du är redo att börja testa.

Dåligt Gateway-fel?

Om du anger adressen i din webbläsare kommer du sannolikt att få ett 502 Bad Gateway-fel. Det kan orsakas av felaktigt konfigurerade UNIX-socketbehörigheter, eller så kan det bero på mer komplexa problem relaterade till åtkomstkontroll i SELinux.

I nginx-felloggen kan du se en rad så här:

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"

Testar vi Gunicorn direkt får vi ett tomt svar.

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

Låt oss ta reda på varför detta händer. Om du öppnar loggen kommer du sannolikt att se att problemet är relaterat till SELinux. Eftersom vi kör en demon för vilken ingen policy har skapats, är den markerad som init_t. Låt oss testa denna teori i praktiken.

sudo setenforce 0

Allt detta kan orsaka kritik och blodtårar, men detta är bara att felsöka prototypen. Låt oss inaktivera kontrollen bara för att försäkra oss om att detta är problemet, varefter vi kommer att återföra allt till sin plats.

Genom att uppdatera sidan i webbläsaren eller köra om vårt curl-kommando kan du se Django-testsidan.

Så, efter att ha sett till att allt fungerar och det inte finns några fler behörighetsproblem, aktiverar vi SELinux igen.

sudo setenforce 1

Jag kommer inte att prata om audit2allow eller att skapa varningsbaserade policyer med sepolgen här, eftersom det inte finns någon faktisk Django-applikation för tillfället, så det finns ingen komplett karta över vad Gunicorn kanske vill komma åt och vad den ska neka åtkomst till. Därför är det nödvändigt att hålla SELinux igång för att skydda systemet, samtidigt som applikationen kan köras och lämna meddelanden i granskningsloggen så att den faktiska policyn sedan kan skapas från dem.

Ange tillåtande domäner

Alla har inte hört talas om tillåtna domäner i SELinux, men de är inget nytt. Många arbetade till och med med dem utan att ens inse det. När en policy skapas baserat på granskningsmeddelanden representerar den skapade policyn den lösta domänen. Låt oss försöka skapa en enkel tillståndspolicy.

För att skapa en specifik tillåten domän för Gunicorn behöver du någon form av policy, och du måste också markera lämpliga filer. Dessutom behövs verktyg för att sammanställa nya policyer.

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 kommer till en anpassad applikation eller applikationer som skickas utan policyer som redan har skapats. I det här fallet kommer den tillåtna domänpolicyn för Gunicorn att vara så enkel som möjligt - deklarera en huvudtyp (gunicorn_t), deklarera en typ som vi kommer att använda för att markera flera körbara filer (gunicorn_exec_t), och ställ sedan in en övergång för systemet att markera korrekt köra processer. Den sista raden ställer in policyn som aktiverad som standard när den läses in.

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;

Du kan kompilera denna policyfil och lägga till den i ditt system.

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 än vad vår okända demon har å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 förhindrar Nginx från att skriva data till UNIX-socket som används av Gunicorn. Vanligtvis börjar politiken i sådana fall att förändras, men det finns andra utmaningar framför sig. Du kan också ändra domäninställningarna från en begränsningsdomän till en behörighetsdomän. Låt oss nu flytta httpd_t till behörighetsdomänen. Detta kommer att ge Nginx den nödvändiga åtkomsten och vi kan fortsätta med ytterligare felsökningsarbete.

sudo semanage permissive -a httpd_t

Så när du väl har lyckats hålla SELinux skyddad (du borde verkligen inte lämna ett SELinux-projekt i begränsat läge) och behörighetsdomänerna är inlästa, måste du ta reda på vad som exakt behöver markeras som gunicorn_exec_t för att få allt att fungera korrekt igen. Låt oss prova att besöka webbplatsen för att se nya meddelanden om åtkomstbegränsningar.

sudo ausearch -m AVC -c gunicorn

Du kommer att se många meddelanden som innehåller 'comm="gunicorn"' som gör olika saker på filer i /srv/djangoapp, så detta är uppenbarligen ett av kommandona värt att flagga.

Men dessutom visas ett meddelande som detta:

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 du tittar på statusen för gunicorn-tjänsten eller kör ps-kommandot kommer du inte att se några pågående processer. Det ser ut som att Gunicorn försöker komma åt Python-tolken i vår virtualenv-miljö, möjligen för att köra arbetarskript. Så låt oss nu markera dessa två körbara filer och kontrollera om vi kan öppna vår Django-testsida.

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

Gunicorn-tjänsten måste startas om innan den nya taggen kan väljas. Du kan starta om den direkt eller stoppa tjänsten och låta socket starta den när du öppnar sidan i webbläsaren. Verifiera att processer har fått rätt etiketter med ps.

ps -efZ | grep gunicorn

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

Om du tittar på AVC-meddelandena nu, innehåller det sista meddelandet permissive=1 för allt relaterat till applikationen och permissive=0 för resten av systemet. Om du förstår vilken typ av åtkomst en riktig applikation behöver kan du snabbt hitta det bästa sättet att lösa sådana problem. Men tills dess är det bäst att hålla systemet säkert och få en tydlig, användbar granskning av Django-projektet.

sudo ausearch -m AVC

Hände!

Ett fungerande Django-projekt har dykt upp med en frontend baserad på Nginx och Gunicorn WSGI. Vi konfigurerade Python 3 och PostgreSQL 10 från RHEL 8 Beta-förråd. Nu kan du gå vidare och skapa (eller helt enkelt distribuera) Django-applikationer eller utforska andra tillgängliga verktyg i RHEL 8 Beta för att automatisera konfigurationsprocessen, förbättra prestanda eller till och med behålla den här konfigurationen.

Källa: will.com

Lägg en kommentar