RHEL 8 Beta Workshop: Bygning af fungerende webapplikationer

RHEL 8 Beta tilbyder udviklere mange nye funktioner, som kan tage sider, men at lære nye ting er altid bedre i praksis, så nedenfor tilbyder vi en workshop om faktisk at skabe en applikationsinfrastruktur baseret på Red Hat Enterprise Linux 8 Beta.

RHEL 8 Beta Workshop: Bygning af fungerende webapplikationer

Lad os tage Python, et populært programmeringssprog blandt udviklere, som basis, en kombination af Django og PostgreSQL, en ret almindelig kombination til at skabe applikationer, og konfigurere RHEL 8 Beta til at arbejde med dem. Så tilføjer vi et par mere (uklassificerede) ingredienser.

Testmiljøet vil ændre sig, fordi det er interessant at udforske mulighederne for automatisering, arbejde med containere og prøvemiljøer med flere servere. For at komme i gang med et nyt projekt, kan du starte med at lave en lille, simpel prototype i hånden, så du kan se præcis, hvad der skal ske, og hvordan det interagerer, og derefter gå videre til at automatisere og skabe mere komplekse konfigurationer. I dag taler vi om oprettelsen af ​​en sådan prototype.

Lad os starte med at implementere RHEL 8 Beta VM-billedet. Du kan installere en virtuel maskine fra bunden eller bruge KVM-gæstebilledet, der er tilgængeligt med dit Beta-abonnement. Når du bruger et gæstebillede, skal du konfigurere en virtuel cd, der vil indeholde metadata og brugerdata til cloud-initialisering (cloud-init). Du behøver ikke at gøre noget særligt med diskstrukturen eller tilgængelige pakker; enhver konfiguration vil gøre det.

Lad os se nærmere på hele processen.

Installation af Django

Med den nyeste version af Django skal du bruge et virtuelt miljø (virtualenv) med Python 3.5 eller nyere. I beta-noterne kan du se, at Python 3.6 er tilgængelig, lad os tjekke, om dette virkelig er tilfældet:

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

Red Hat bruger aktivt Python som et systemværktøjssæt i RHEL, så hvorfor resulterer dette?

Faktum er, at mange Python-udviklere stadig overvejer overgangen fra Python 2 til Python 2, mens Python 3 selv er under aktiv udvikling, og flere og flere nye versioner dukker hele tiden op. Derfor, for at imødekomme behovet for stabile systemværktøjer og samtidig tilbyde brugerne adgang til forskellige nye versioner af Python, blev system Python flyttet ind i en ny pakke og gav mulighed for at installere både Python 2.7 og 3.6. Mere information om ændringerne og hvorfor de blev foretaget kan findes i publikationen i Langdon Whites blog (Langdon White).

Så for at få Python til at fungere, behøver du kun at installere to pakker, med python3-pip inkluderet som en afhængighed.

sudo yum install python36 python3-virtualenv

Hvorfor ikke bruge direkte modulkald som Langdon foreslår og installere pip3? Med den kommende automatisering i tankerne, er det kendt, at Ansible vil kræve pip installeret for at køre, da pip-modulet ikke understøtter virtualenvs med en brugerdefineret pip eksekverbar.

Med en fungerende python3-tolk til din rådighed, kan du fortsætte med Django-installationsprocessen og have et fungerende system sammen med vores andre komponenter. Der er mange implementeringsmuligheder tilgængelige på internettet. Der er én version præsenteret her, men brugere kan bruge deres egne processer.

Vi installerer PostgreSQL- og Nginx-versionerne, der er tilgængelige i RHEL 8 som standard ved hjælp af Yum.

sudo yum install nginx postgresql-server

PostgreSQL vil kræve psycopg2, men det skal kun være tilgængeligt i et virtualenv-miljø, så vi installerer det ved hjælp af pip3 sammen med Django og Gunicorn. Men først skal vi konfigurere virtualenv.

Der er altid en masse debat om emnet at vælge det rigtige sted at installere Django-projekter, men når du er i tvivl, kan du altid henvende dig til Linux Filesystem Hierarchy Standard. Specifikt siger FHS, at /srv bruges til at: "lagre værtsspecifikke data - data, som systemet producerer, såsom webserverdata og scripts, data gemt på FTP-servere og kontrolsystemlagre." versioner (vises i FHS -2.3 i 2004)."

Dette er præcis vores tilfælde, så vi lægger alt, hvad vi har brug for, i /srv, som ejes af vores applikationsbruger (cloud-bruger).

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

Opsætning af PostgreSQL og Django er let: opret en database, opret en bruger, konfigurer tilladelser. En ting at huske på, når du først installerer PostgreSQL, er postgresql-setup-scriptet, der er installeret med postgresql-server-pakken. Dette script hjælper dig med at udføre grundlæggende opgaver forbundet med databaseklyngeadministration, såsom klyngeinitialisering eller opgraderingsprocessen. For at konfigurere en ny PostgreSQL-instans på et RHEL-system skal vi køre kommandoen:

sudo /usr/bin/postgresql-setup -initdb

Du kan derefter starte PostgreSQL ved hjælp af systemd, oprette en database og opsætte et projekt i Django. Husk at genstarte PostgreSQL efter at have foretaget ændringer i klientgodkendelseskonfigurationsfilen (normalt pg_hba.conf) for at konfigurere adgangskodelagring for applikationsbrugeren. Hvis du støder på andre problemer, skal du sørge for at ændre IPv4- og IPv6-indstillingerne 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 at have konfigureret filen settings.py i projektet og opsat databasekonfigurationen, kan du starte udviklingsserveren for at sikre, at alt fungerer. Efter start af udviklingsserveren er det en god idé at oprette en admin-bruger for at teste forbindelsen til databasen.

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

WSGI? Wai?

Udviklingsserveren er nyttig til test, men for at køre applikationen skal du konfigurere den passende server og proxy til Web Server Gateway Interface (WSGI). Der er flere almindelige kombinationer, for eksempel Apache HTTPD med uWSGI eller Nginx med Gunicorn.

Job Server Gateway Interface er at videresende anmodninger fra webserveren til Python-webframeworket. WSGI er et levn fra den frygtelige fortid, da CGI-motorer eksisterede, og i dag er WSGI de facto-standarden, uanset hvilken webserver eller Python-framework, der blev brugt. Men på trods af dens udbredte brug, er der stadig mange nuancer, når man arbejder med disse rammer, og mange valgmuligheder. I dette tilfælde vil vi forsøge at etablere interaktion mellem Gunicorn og Nginx via en socket.

Da begge disse komponenter er installeret på den samme server, lad os prøve at bruge en UNIX-socket i stedet for en netværkssocket. Da kommunikation under alle omstændigheder kræver en socket, lad os prøve at tage et skridt mere og konfigurere socket-aktivering til Gunicorn via systemd.

Processen med at oprette socket-aktiverede tjenester er ret enkel. Først oprettes en enhedsfil, der indeholder et ListenStream-direktiv, der peger på det punkt, hvor UNIX-socket vil blive oprettet, derefter en enhedsfil for tjenesten, hvor Requires-direktivet peger på socket-enhedsfilen. Så i serviceenhedsfilen er der kun tilbage at kalde Gunicorn fra det virtuelle miljø og oprette en WSGI-binding til UNIX-socket og Django-applikationen.

Her er nogle eksempler på enhedsfiler, som du kan bruge som grundlag. Først sætter vi stikkontakten op.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Nu skal du konfigurere Gunicorn-dæmonen.

[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

For Nginx er det et simpelt spørgsmål om at oprette proxy-konfigurationsfiler og opsætte en mappe til at gemme statisk indhold, hvis du bruger en. I RHEL er Nginx-konfigurationsfiler placeret i /etc/nginx/conf.d. Du kan kopiere følgende eksempel ind i filen /etc/nginx/conf.d/default.conf og starte tjenesten. Sørg for at indstille servernavnet til at matche dit værtsnavn.

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

Start Gunicorn-socket og Nginx ved hjælp af systemd, og du er klar til at begynde at teste.

Dårlig gateway-fejl?

Hvis du indtaster adressen i din browser, vil du højst sandsynligt modtage en 502 Bad Gateway-fejl. Det kan være forårsaget af forkert konfigurerede UNIX-socket-tilladelser, eller det kan skyldes mere komplekse problemer relateret til adgangskontrol i SELinux.

I nginx fejlloggen kan du se en linje som denne:

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"

Hvis vi tester Gunicorn direkte, får vi et tomt svar.

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

Lad os finde ud af, hvorfor dette sker. Hvis du åbner loggen, vil du højst sandsynligt se, at problemet er relateret til SELinux. Da vi kører en dæmon, for hvilken der ikke er oprettet nogen politik, er den markeret som init_t. Lad os afprøve denne teori i praksis.

sudo setenforce 0

Alt dette kan forårsage kritik og tårer af blod, men dette er blot fejlretning af prototypen. Lad os deaktivere kontrollen bare for at sikre, at dette er problemet, hvorefter vi returnerer alt tilbage til dets plads.

Ved at opdatere siden i browseren eller køre vores curl-kommando igen, kan du se Django-testsiden.

Så efter at have sikret os, at alt fungerer, og der ikke er flere tilladelsesproblemer, aktiverer vi SELinux igen.

sudo setenforce 1

Jeg vil ikke tale om audit2allow eller oprette alarmbaserede politikker med sepolgen her, da der ikke er nogen egentlig Django-applikation i øjeblikket, så der er ikke noget komplet kort over, hvad Gunicorn måske vil have adgang til, og hvad den skal nægte adgang til. Derfor er det nødvendigt at holde SELinux kørende for at beskytte systemet, samtidig med at applikationen kan køre og efterlade beskeder i revisionsloggen, så den egentlige politik så kan oprettes ud fra dem.

Angivelse af tilladelige domæner

Ikke alle har hørt om tilladte domæner i SELinux, men de er ikke noget nyt. Mange arbejdede endda med dem uden selv at være klar over det. Når en politik oprettes baseret på revisionsmeddelelser, repræsenterer den oprettede politik det løste domæne. Lad os prøve at skabe en simpel tilladelsespolitik.

For at oprette et specifikt tilladt domæne til Gunicorn skal du have en form for politik, og du skal også markere de relevante filer. Derudover er der brug for værktøjer til at sammensætte nye politikker.

sudo yum install selinux-policy-devel

Den tilladte domænemekanisme er et fantastisk værktøj til at identificere problemer, især når det kommer til en brugerdefineret applikation eller applikationer, der sendes uden allerede oprettede politikker. I dette tilfælde vil den tilladte domænepolitik for Gunicorn være så enkel som muligt - erklær en hovedtype (gunicorn_t), erklær en type, vi vil bruge til at markere flere eksekverbare filer (gunicorn_exec_t), og opsæt derefter en overgang for systemet til at markere korrekt kørende processer. Den sidste linje angiver politikken som aktiveret som standard på det tidspunkt, den indlæses.

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 kompilere denne politikfil og tilføje den til dit system.

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

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

Lad os tjekke om SELinux blokerer noget andet end det, vores ukendte dæmon har adgang til.

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 forhindrer Nginx i at skrive data til UNIX-socket, der bruges af Gunicorn. I sådanne tilfælde begynder politikkerne typisk at ændre sig, men der er andre udfordringer forude. Du kan også ændre domæneindstillingerne fra et begrænsningsdomæne til et tilladelsesdomæne. Lad os nu flytte httpd_t til tilladelsesdomænet. Dette vil give Nginx den nødvendige adgang, og vi kan fortsætte med yderligere fejlfindingsarbejde.

sudo semanage permissive -a httpd_t

Så når du har formået at holde SELinux beskyttet (du bør virkelig ikke forlade et SELinux-projekt i begrænset tilstand) og tilladelsesdomænerne er indlæst, skal du finde ud af, hvad der præcist skal markeres som gunicorn_exec_t for at få alt til at fungere korrekt en gang til. Lad os prøve at besøge webstedet for at se nye beskeder om adgangsbegrænsninger.

sudo ausearch -m AVC -c gunicorn

Du vil se en masse beskeder, der indeholder 'comm="gunicorn"', der gør forskellige ting på filer i /srv/djangoapp, så dette er åbenbart en af ​​kommandoerne, der er værd at markere.

Men derudover vises en besked som denne:

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

Hvis du ser på status for gunicorn-tjenesten eller kører ps-kommandoen, vil du ikke se nogen kørende processer. Det ser ud til, at gunicorn forsøger at få adgang til Python-fortolkeren i vores virtualenv-miljø, muligvis for at køre arbejderscripts. Så lad os nu markere disse to eksekverbare filer og tjekke, om vi kan åbne vores Django-testside.

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

Gunicorn-tjenesten skal genstartes, før det nye tag kan vælges. Du kan genstarte den med det samme eller stoppe tjenesten og lade stikket starte den, når du åbner siden i browseren. Bekræft, at processer har modtaget de korrekte etiketter ved hjælp af ps.

ps -efZ | grep gunicorn

Glem ikke at oprette en normal SELinux-politik senere!

Hvis du ser på AVC-meddelelserne nu, indeholder den sidste besked permissive=1 for alt relateret til applikationen og permissive=0 for resten af ​​systemet. Hvis du forstår, hvilken slags adgang en rigtig applikation har brug for, kan du hurtigt finde den bedste måde at løse sådanne problemer på. Men indtil da er det bedst at holde systemet sikkert og få en klar, brugbar revision af Django-projektet.

sudo ausearch -m AVC

skete!

Et fungerende Django-projekt er dukket op med en frontend baseret på Nginx og Gunicorn WSGI. Vi konfigurerede Python 3 og PostgreSQL 10 fra RHEL 8 Beta-lagrene. Nu kan du gå videre og oprette (eller blot implementere) Django-applikationer eller udforske andre tilgængelige værktøjer i RHEL 8 Beta for at automatisere konfigurationsprocessen, forbedre ydeevnen eller endda containerisere denne konfiguration.

Kilde: www.habr.com

Tilføj en kommentar