Lokakarya RHEL 8 Beta: Membangun aplikasi web yang berfungsi

RHEL 8 Beta menawarkan banyak fitur baru kepada pengembang, yang daftarnya bisa memakan waktu berhalaman-halaman, namun, mempelajari hal-hal baru selalu lebih baik dalam praktiknya, jadi di bawah ini kami menawarkan lokakarya tentang cara membuat infrastruktur aplikasi berdasarkan Red Hat Enterprise Linux 8 Beta.

Lokakarya RHEL 8 Beta: Membangun aplikasi web yang berfungsi

Mari kita ambil Python, bahasa pemrograman populer di kalangan pengembang, sebagai dasar, kombinasi Django dan PostgreSQL, kombinasi yang cukup umum untuk membuat aplikasi, dan konfigurasikan RHEL 8 Beta untuk bekerja dengannya. Kemudian kami akan menambahkan beberapa bahan lagi (tidak terklasifikasi).

Lingkungan pengujian akan berubah, karena menarik untuk mengeksplorasi kemungkinan otomatisasi, bekerja dengan container, dan mencoba lingkungan dengan banyak server. Untuk memulai proyek baru, Anda dapat memulai dengan membuat prototipe kecil dan sederhana dengan tangan sehingga Anda dapat melihat dengan tepat apa yang perlu terjadi dan bagaimana interaksinya, lalu melanjutkan ke otomatisasi dan membuat konfigurasi yang lebih kompleks. Hari ini kita berbicara tentang pembuatan prototipe semacam itu.

Mari kita mulai dengan men-deploy image RHEL 8 Beta VM. Anda dapat menginstal mesin virtual dari awal, atau menggunakan image tamu KVM yang tersedia dengan langganan Beta Anda. Saat menggunakan image tamu, Anda perlu mengonfigurasi CD virtual yang akan berisi metadata dan data pengguna untuk inisialisasi cloud (cloud-init). Anda tidak perlu melakukan sesuatu yang khusus dengan struktur disk atau paket yang tersedia; konfigurasi apa pun bisa digunakan.

Mari kita lihat lebih dekat keseluruhan prosesnya.

Menginstal Django

Dengan Django versi terbaru, Anda memerlukan lingkungan virtual (virtualenv) dengan Python 3.5 atau lebih baru. Dalam catatan Beta Anda dapat melihat bahwa Python 3.6 tersedia, mari kita periksa apakah memang demikian:

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

Red Hat secara aktif menggunakan Python sebagai perangkat sistem di RHEL, jadi mengapa hal ini terjadi?

Faktanya adalah banyak pengembang Python yang masih memikirkan transisi dari Python 2 ke Python 2, sementara Python 3 sendiri sedang dalam pengembangan aktif, dan semakin banyak versi baru yang terus bermunculan. Oleh karena itu, untuk memenuhi kebutuhan alat sistem yang stabil sambil menawarkan akses kepada pengguna ke berbagai versi baru Python, sistem Python dipindahkan ke paket baru dan menyediakan kemampuan untuk menginstal Python 2.7 dan 3.6. Informasi lebih lanjut tentang perubahan dan alasan perubahan tersebut dilakukan dapat ditemukan dalam publikasi di Blog Langdon White (Langdon Putih).

Jadi, agar Python dapat berfungsi, Anda hanya perlu menginstal dua paket, dengan python3-pip disertakan sebagai dependensi.

sudo yum install python36 python3-virtualenv

Mengapa tidak menggunakan panggilan modul langsung seperti yang disarankan Langdon dan menginstal pip3? Mengingat otomatisasi yang akan datang, diketahui bahwa Ansible akan memerlukan pip diinstal untuk dijalankan, karena modul pip tidak mendukung virtualenvs dengan pip khusus yang dapat dieksekusi.

Dengan juru bahasa python3 yang berfungsi yang Anda inginkan, Anda dapat melanjutkan proses instalasi Django dan memiliki sistem kerja bersama dengan komponen kami yang lain. Ada banyak pilihan implementasi yang tersedia di Internet. Ada satu versi yang disajikan di sini, namun pengguna dapat menggunakan proses mereka sendiri.

Kami akan menginstal versi PostgreSQL dan Nginx yang tersedia di RHEL 8 secara default menggunakan Yum.

sudo yum install nginx postgresql-server

PostgreSQL akan membutuhkan psycopg2, tetapi harus tersedia hanya di lingkungan virtualenv, jadi kami akan menginstalnya menggunakan pip3 bersama dengan Django dan Gunicorn. Tapi pertama-tama kita perlu menyiapkan virtualenv.

Selalu ada banyak perdebatan mengenai topik memilih tempat yang tepat untuk menginstal proyek Django, tetapi jika ragu, Anda selalu dapat beralih ke Linux Filesystem Hierarchy Standard. Secara khusus, FHS mengatakan bahwa /srv digunakan untuk: β€œmenyimpan data spesifik hostβ€”data yang dihasilkan sistem, seperti data dan skrip server web, data yang disimpan di server FTP, dan mengontrol repositori sistem.” (muncul di FHS -2.3 pada tahun 2004).

Inilah kasus kami, jadi kami memasukkan semua yang kami perlukan ke dalam /srv, yang dimiliki oleh pengguna aplikasi kami (pengguna cloud).

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

Menyiapkan PostgreSQL dan Django mudah: membuat database, membuat pengguna, mengkonfigurasi izin. Satu hal yang perlu diingat ketika pertama kali menginstal PostgreSQL adalah skrip pengaturan postgresql yang diinstal dengan paket server postgresql. Skrip ini membantu Anda melakukan tugas dasar yang terkait dengan administrasi klaster database, seperti inisialisasi klaster atau proses pemutakhiran. Untuk mengonfigurasi instance PostgreSQL baru pada sistem RHEL, kita perlu menjalankan perintah:

sudo /usr/bin/postgresql-setup -initdb

Anda kemudian dapat memulai PostgreSQL menggunakan systemd, membuat database, dan menyiapkan proyek di Django. Ingatlah untuk memulai ulang PostgreSQL setelah melakukan perubahan pada file konfigurasi otentikasi klien (biasanya pg_hba.conf) untuk mengonfigurasi penyimpanan kata sandi bagi pengguna aplikasi. Jika Anda mengalami kesulitan lain, pastikan untuk mengubah pengaturan IPv4 dan IPv6 di file 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

Dalam file /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

Dalam berkas /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 }}',
   }
}

Setelah mengonfigurasi file settings.py di proyek dan menyiapkan konfigurasi database, Anda dapat memulai server pengembangan untuk memastikan semuanya berfungsi. Setelah memulai server pengembangan, sebaiknya buat pengguna admin untuk menguji koneksi ke database.

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

WSGI? Tunggu?

Server pengembangan berguna untuk pengujian, tetapi untuk menjalankan aplikasi Anda harus mengkonfigurasi server dan proksi yang sesuai untuk Web Server Gateway Interface (WSGI). Ada beberapa kombinasi umum, misalnya Apache HTTPD dengan uWSGI atau Nginx dengan Gunicorn.

Tugas Antarmuka Gerbang Server Web adalah meneruskan permintaan dari server web ke kerangka web Python. WSGI adalah peninggalan masa lalu yang buruk ketika mesin CGI masih ada, dan saat ini WSGI adalah standar de facto, terlepas dari server web atau kerangka Python yang digunakan. Namun meskipun penggunaannya tersebar luas, masih banyak perbedaan saat bekerja dengan kerangka kerja ini, dan banyak pilihan. Dalam hal ini, kami akan mencoba menjalin interaksi antara Gunicorn dan Nginx melalui soket.

Karena kedua komponen ini diinstal pada server yang sama, mari kita coba menggunakan soket UNIX daripada soket jaringan. Karena komunikasi memerlukan soket, mari kita coba mengambil satu langkah lagi dan mengkonfigurasi aktivasi soket untuk Gunicorn melalui systemd.

Proses pembuatan layanan yang diaktifkan soket cukup sederhana. Pertama, file unit dibuat yang berisi direktif ListenStream yang menunjuk ke titik di mana soket UNIX akan dibuat, kemudian file unit untuk layanan di mana direktif Requires akan menunjuk ke file unit soket. Kemudian, dalam file unit layanan, yang tersisa hanyalah memanggil Gunicorn dari lingkungan virtual dan membuat pengikatan WSGI untuk soket UNIX dan aplikasi Django.

Berikut beberapa contoh file unit yang dapat Anda gunakan sebagai dasar. Pertama kita menyiapkan soketnya.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Sekarang Anda perlu mengkonfigurasi daemon Gunicorn.

[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

Untuk Nginx, cukup dengan membuat file konfigurasi proxy dan menyiapkan direktori untuk menyimpan konten statis jika Anda menggunakannya. Di RHEL, file konfigurasi Nginx terletak di /etc/nginx/conf.d. Anda dapat menyalin contoh berikut ke dalam file /etc/nginx/conf.d/default.conf dan memulai layanan. Pastikan untuk mengatur server_name agar sesuai dengan nama host Anda.

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

Mulai soket Gunicorn dan Nginx menggunakan systemd dan Anda siap untuk memulai pengujian.

Kesalahan Gateway buruk?

Jika Anda memasukkan alamat tersebut ke browser Anda, kemungkinan besar Anda akan menerima kesalahan 502 Bad Gateway. Ini mungkin disebabkan oleh izin soket UNIX yang tidak dikonfigurasi dengan benar, atau mungkin karena masalah yang lebih kompleks terkait dengan kontrol akses di SELinux.

Di log kesalahan nginx Anda dapat melihat baris seperti ini:

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"

Jika kita menguji Gunicorn secara langsung, kita akan mendapatkan jawaban yang kosong.

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

Mari kita cari tahu mengapa ini terjadi. Jika Anda membuka log, kemungkinan besar Anda akan melihat bahwa masalahnya terkait dengan SELinux. Karena kita menjalankan daemon yang kebijakannya belum dibuat, maka daemon tersebut ditandai sebagai init_t. Mari kita uji teori ini dalam praktik.

sudo setenforce 0

Semua ini mungkin menimbulkan kritik dan air mata darah, tetapi ini hanya men-debug prototipe. Mari kita nonaktifkan pemeriksaan hanya untuk memastikan bahwa ini masalahnya, setelah itu kita akan mengembalikan semuanya ke tempatnya.

Dengan menyegarkan halaman di browser atau menjalankan kembali perintah curl kami, Anda dapat melihat halaman pengujian Django.

Jadi, setelah memastikan semuanya berfungsi dan tidak ada lagi masalah izin, kami mengaktifkan kembali SELinux.

sudo setenforce 1

Saya tidak akan berbicara tentang audit2allow atau membuat kebijakan berbasis peringatan dengan sepolgen di sini, karena tidak ada aplikasi Django yang sebenarnya saat ini, jadi tidak ada peta lengkap tentang apa yang mungkin ingin diakses oleh Gunicorn dan apa yang harus ditolak aksesnya. Oleh karena itu, SELinux perlu tetap berjalan untuk melindungi sistem, sekaligus mengizinkan aplikasi untuk berjalan dan meninggalkan pesan di log audit sehingga kebijakan sebenarnya dapat dibuat dari aplikasi tersebut.

Menentukan domain permisif

Tidak semua orang pernah mendengar tentang domain yang diizinkan di SELinux, tapi itu bukanlah hal baru. Banyak yang bahkan bekerja dengan mereka tanpa menyadarinya. Ketika kebijakan dibuat berdasarkan pesan audit, kebijakan yang dibuat mewakili domain yang diselesaikan. Mari kita coba membuat kebijakan perizinan sederhana.

Untuk membuat domain khusus yang diizinkan untuk Gunicorn, Anda memerlukan semacam kebijakan, dan Anda juga perlu menandai file yang sesuai. Selain itu, diperlukan alat untuk menyusun kebijakan baru.

sudo yum install selinux-policy-devel

Mekanisme domain yang diizinkan adalah alat yang hebat untuk mengidentifikasi masalah, terutama jika menyangkut aplikasi khusus atau aplikasi yang dikirimkan tanpa kebijakan yang sudah dibuat. Dalam hal ini, kebijakan domain yang diizinkan untuk Gunicorn akan sesederhana mungkin - mendeklarasikan tipe utama (gunicorn_t), mendeklarasikan tipe yang akan kita gunakan untuk menandai beberapa executable (gunicorn_exec_t), dan kemudian menyiapkan transisi agar sistem dapat menandai dengan benar proses yang berjalan. Baris terakhir menetapkan kebijakan sebagai diaktifkan secara default pada saat kebijakan dimuat.

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;

Anda dapat mengkompilasi file kebijakan ini dan menambahkannya ke sistem Anda.

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

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

Mari kita periksa apakah SELinux memblokir sesuatu selain yang diakses oleh daemon tak dikenal kita.

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 mencegah Nginx menulis data ke soket UNIX yang digunakan oleh Gunicorn. Biasanya, dalam kasus seperti ini, kebijakan mulai berubah, namun masih terdapat tantangan lain yang akan datang. Anda juga dapat mengubah pengaturan domain dari domain pembatasan menjadi domain izin. Sekarang mari kita pindahkan httpd_t ke domain izin. Ini akan memberi Nginx akses yang diperlukan dan kita dapat melanjutkan pekerjaan debugging lebih lanjut.

sudo semanage permissive -a httpd_t

Jadi, setelah Anda berhasil menjaga SELinux tetap terlindungi (Anda sebaiknya tidak meninggalkan proyek SELinux dalam mode terbatas) dan domain izin dimuat, Anda perlu mencari tahu apa sebenarnya yang perlu ditandai sebagai gunicorn_exec_t agar semuanya berfungsi dengan baik lagi. Mari kita coba kunjungi websitenya untuk melihat pesan baru tentang pembatasan akses.

sudo ausearch -m AVC -c gunicorn

Anda akan melihat banyak pesan berisi 'comm="gunicorn"' yang melakukan berbagai hal pada berkas di /srv/djangoapp, jadi ini jelas merupakan salah satu perintah yang patut ditandai.

Namun selain itu, muncul pesan seperti ini:

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

Jika Anda melihat status layanan gunicorn atau menjalankan perintah ps, Anda tidak akan melihat proses yang berjalan. Sepertinya gunicorn mencoba mengakses juru bahasa Python di lingkungan virtualenv kami, mungkin untuk menjalankan skrip pekerja. Jadi sekarang mari tandai dua berkas yang dapat dieksekusi ini dan periksa apakah kita dapat membuka halaman pengujian Django kita.

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

Layanan gunicorn perlu dimulai ulang sebelum tag baru dapat dipilih. Anda dapat segera memulai ulang atau menghentikan layanan dan membiarkan soket memulainya saat Anda membuka situs di browser. Verifikasi bahwa proses telah menerima label yang benar menggunakan ps.

ps -efZ | grep gunicorn

Jangan lupa untuk membuat kebijakan SELinux yang normal nanti!

Jika Anda melihat pesan AVC sekarang, pesan terakhir berisi permisif=1 untuk segala sesuatu yang berhubungan dengan aplikasi, dan permisif=0 untuk seluruh sistem. Jika Anda memahami jenis akses yang dibutuhkan aplikasi sebenarnya, Anda dapat dengan cepat menemukan cara terbaik untuk menyelesaikan masalah tersebut. Namun sampai saat itu tiba, yang terbaik adalah menjaga sistem tetap aman dan mendapatkan audit proyek Django yang jelas dan dapat digunakan.

sudo ausearch -m AVC

Telah terjadi!

Proyek Django yang berfungsi telah muncul dengan frontend berdasarkan Nginx dan Gunicorn WSGI. Kami mengonfigurasi Python 3 dan PostgreSQL 10 dari repositori RHEL 8 Beta. Sekarang Anda dapat bergerak maju dan membuat (atau menyebarkan) aplikasi Django atau menjelajahi alat lain yang tersedia di RHEL 8 Beta untuk mengotomatiskan proses konfigurasi, meningkatkan kinerja, atau bahkan memasukkan konfigurasi ini ke dalam container.

Sumber: www.habr.com

Tambah komentar