کارگاه RHEL 8 بتا: ساخت برنامه های کاربردی وب

RHEL 8 Beta بسیاری از ویژگی‌های جدید را به توسعه‌دهندگان ارائه می‌دهد، فهرست‌بندی آن‌ها ممکن است صفحاتی را به خود اختصاص دهد، با این حال، یادگیری چیزهای جدید همیشه در عمل بهتر است، بنابراین در زیر یک کارگاه آموزشی در مورد ایجاد زیرساخت برنامه کاربردی مبتنی بر Red Hat Enterprise Linux 8 Beta ارائه می‌دهیم.

کارگاه RHEL 8 بتا: ساخت برنامه های کاربردی وب

بیایید پایتون، یک زبان برنامه نویسی محبوب در میان توسعه دهندگان را به عنوان پایه در نظر بگیریم، ترکیبی از جنگو و PostgreSQL، ترکیبی نسبتاً رایج برای ایجاد برنامه ها، و RHEL 8 بتا را برای کار با آنها پیکربندی کنیم. سپس چند ماده دیگر (طبقه بندی نشده) اضافه می کنیم.

محیط آزمایش تغییر خواهد کرد، زیرا بررسی امکانات اتوماسیون، کار با کانتینرها و آزمایش محیط‌ها با چندین سرور جالب است. برای شروع کار با یک پروژه جدید، می توانید با ایجاد یک نمونه اولیه کوچک و ساده با دست شروع کنید تا بتوانید دقیقاً ببینید چه اتفاقی باید بیفتد و چگونه تعامل می کند و سپس به سمت خودکارسازی و ایجاد تنظیمات پیچیده تر بروید. امروز ما در مورد ایجاد چنین نمونه اولیه صحبت می کنیم.

بیایید با استقرار تصویر RHEL 8 Beta VM شروع کنیم. می توانید یک ماشین مجازی را از ابتدا نصب کنید یا از تصویر مهمان KVM که با اشتراک بتا خود در دسترس است استفاده کنید. هنگام استفاده از یک تصویر مهمان، باید یک سی دی مجازی را پیکربندی کنید که حاوی متادیتا و داده های کاربر برای مقداردهی اولیه ابر (cloud-init) باشد. شما نیازی به انجام کار خاصی با ساختار دیسک یا بسته های موجود ندارید؛ هر پیکربندی انجام خواهد داد.

بیایید نگاهی دقیق تر به کل فرآیند بیندازیم.

نصب جنگو

با جدیدترین نسخه جنگو، به یک محیط مجازی (virtualenv) با پایتون 3.5 یا بالاتر نیاز دارید. در یادداشت‌های بتا می‌بینید که پایتون 3.6 در دسترس است، بیایید بررسی کنیم که آیا واقعاً چنین است:

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

Red Hat به طور فعال از Python به عنوان یک جعبه ابزار سیستم در RHEL استفاده می کند، پس چرا این نتیجه می شود؟

واقعیت این است که بسیاری از توسعه دهندگان پایتون هنوز در حال فکر انتقال از پایتون 2 به پایتون 2 هستند، در حالی که پایتون 3 خود در حال توسعه فعال است و نسخه های جدید بیشتری به طور مداوم ظاهر می شوند. بنابراین، برای رفع نیاز به ابزارهای سیستم پایدار و در عین حال دسترسی کاربران به نسخه‌های جدید مختلف پایتون، سیستم پایتون به یک بسته جدید منتقل شد و امکان نصب هر دو پایتون 2.7 و 3.6 را فراهم کرد. اطلاعات بیشتر در مورد تغییرات و دلیل ایجاد آنها را می توان در نشریه در یافت وبلاگ لنگدون وایت (لنگدون وایت).

بنابراین، برای شروع به کار پایتون، فقط باید دو بسته را نصب کنید که python3-pip به عنوان یک وابستگی گنجانده شده است.

sudo yum install python36 python3-virtualenv

چرا همانطور که لانگدون پیشنهاد می کند از فراخوانی مستقیم ماژول استفاده نکنید و pip3 را نصب نکنید؟ با در نظر گرفتن اتوماسیون آینده، مشخص است که Ansible برای اجرا به پیپ نصب شده نیاز دارد، زیرا ماژول pip از virtualenvs با یک پیپ اجرایی سفارشی پشتیبانی نمی کند.

با یک مترجم python3 که در اختیار شماست، می‌توانید فرآیند نصب جنگو را ادامه دهید و یک سیستم کار به همراه سایر اجزای ما داشته باشید. گزینه های پیاده سازی زیادی در اینترنت وجود دارد. یک نسخه در اینجا ارائه شده است، اما کاربران می توانند از فرآیندهای خود استفاده کنند.

ما نسخه‌های PostgreSQL و Nginx را که در RHEL 8 در دسترس هستند به‌طور پیش‌فرض با استفاده از Yum نصب می‌کنیم.

sudo yum install nginx postgresql-server

PostgreSQL به psycopg2 نیاز دارد، اما باید فقط در یک محیط virtualenv در دسترس باشد، بنابراین ما آن را با استفاده از pip3 همراه با جنگو و گونیکورن نصب خواهیم کرد. اما ابتدا باید virtualenv را راه اندازی کنیم.

همیشه بحث های زیادی در مورد انتخاب مکان مناسب برای نصب پروژه های جنگو وجود دارد، اما در صورت شک، همیشه می توانید به استاندارد سلسله مراتبی سیستم فایل لینوکس مراجعه کنید. به طور خاص، FHS می گوید که /srv برای ذخیره سازی داده های اختصاصی میزبان استفاده می شود - داده هایی که سیستم تولید می کند، مانند داده ها و اسکریپت های سرور وب، داده های ذخیره شده در سرورهای FTP و کنترل مخازن سیستم. -2.3 در سال 2004).

این دقیقاً مورد ما است، بنابراین ما هر آنچه را که نیاز داریم در /srv که متعلق به کاربر برنامه ما (ابر کاربر) است قرار می دهیم.

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

راه اندازی PostgreSQL و Django آسان است: ایجاد یک پایگاه داده، ایجاد یک کاربر، پیکربندی مجوزها. یکی از مواردی که هنگام نصب اولیه PostgreSQL باید در نظر داشت اسکریپت postgresql-setup است که با بسته postgresql-server نصب می شود. این اسکریپت به شما کمک می کند تا کارهای اساسی مرتبط با مدیریت خوشه پایگاه داده، مانند راه اندازی کلاستر یا فرآیند ارتقا را انجام دهید. برای پیکربندی یک نمونه PostgreSQL جدید در یک سیستم RHEL، باید دستور زیر را اجرا کنیم:

sudo /usr/bin/postgresql-setup -initdb

سپس می توانید PostgreSQL را با استفاده از systemd راه اندازی کنید، یک پایگاه داده ایجاد کنید و یک پروژه در جنگو راه اندازی کنید. به یاد داشته باشید که پس از ایجاد تغییرات در فایل پیکربندی احراز هویت مشتری (معمولا pg_hba.conf) PostgreSQL را مجدداً راه اندازی کنید تا ذخیره سازی رمز عبور را برای کاربر برنامه پیکربندی کنید. اگر با مشکلات دیگری مواجه شدید، مطمئن شوید که تنظیمات IPv4 و IPv6 را در فایل 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

در فایل /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

در فایل /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 }}',
   }
}

پس از پیکربندی فایل settings.py در پروژه و تنظیم پیکربندی پایگاه داده، می توانید سرور توسعه را راه اندازی کنید تا مطمئن شوید همه چیز کار می کند. پس از راه اندازی سرور توسعه، ایده خوبی است که یک کاربر مدیریت ایجاد کنید تا اتصال به پایگاه داده را آزمایش کنید.

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

WSGI؟ وای؟

سرور توسعه برای آزمایش مفید است، اما برای اجرای برنامه باید سرور و پروکسی مناسب را برای رابط دروازه وب سرور (WSGI) پیکربندی کنید. چندین ترکیب رایج وجود دارد، به عنوان مثال، Apache HTTPD با uWSGI یا Nginx با Gunicorn.

وظیفه رابط دروازه وب سرور ارسال درخواست ها از وب سرور به چارچوب وب پایتون است. WSGI یادگاری از گذشته وحشتناک زمانی است که موتورهای CGI وجود داشتند، و امروزه WSGI بدون توجه به وب سرور یا چارچوب پایتون مورد استفاده، استاندارد واقعی است. اما با وجود استفاده گسترده از آن، هنوز تفاوت های ظریف زیادی در هنگام کار با این فریمورک ها وجود دارد و انتخاب های زیادی وجود دارد. در این مورد سعی می کنیم تعامل بین Gunicorn و Nginx را از طریق یک سوکت برقرار کنیم.

از آنجایی که هر دوی این مؤلفه ها روی یک سرور نصب شده اند، بیایید سعی کنیم از سوکت یونیکس به جای سوکت شبکه استفاده کنیم. از آنجایی که ارتباط در هر صورت نیاز به یک سوکت دارد، بیایید سعی کنیم یک قدم دیگر برداریم و فعال سازی سوکت را برای Gunicorn از طریق systemd پیکربندی کنیم.

فرآیند ایجاد سرویس های فعال شده سوکت بسیار ساده است. ابتدا یک فایل واحد ایجاد می شود که حاوی یک دستورالعمل ListenStream است که به نقطه ای که سوکت یونیکس در آن ایجاد می شود اشاره می کند، سپس یک فایل واحد برای سرویسی که دستورالعمل Requires در آن به فایل واحد سوکت اشاره می کند. سپس، در فایل واحد سرویس، تنها چیزی که باقی می ماند این است که Gunicorn را از محیط مجازی فراخوانی کنید و یک اتصال WSGI برای سوکت یونیکس و برنامه جنگو ایجاد کنید.

در اینجا چند نمونه از فایل های واحد وجود دارد که می توانید از آنها به عنوان پایه استفاده کنید. ابتدا سوکت را راه اندازی می کنیم.

[Unit]
Description=Gunicorn WSGI socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

اکنون باید دیمون 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

برای Nginx، ایجاد فایل‌های پیکربندی پراکسی و راه‌اندازی دایرکتوری برای ذخیره محتوای استاتیک در صورتی که از آن استفاده می‌کنید، ساده است. در RHEL، فایل های پیکربندی Nginx در /etc/nginx/conf.d قرار دارند. می توانید مثال زیر را در فایل /etc/nginx/conf.d/default.conf کپی کنید و سرویس را راه اندازی کنید. مطمئن شوید که server_name را مطابق با نام میزبان خود تنظیم کنید.

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

سوکت Gunicorn و Nginx را با استفاده از systemd راه اندازی کنید و آماده شروع آزمایش هستید.

خطای دروازه بد؟

اگر آدرس را در مرورگر خود وارد کنید، به احتمال زیاد خطای 502 Bad Gateway را دریافت خواهید کرد. ممکن است ناشی از پیکربندی نادرست مجوزهای سوکت یونیکس باشد، یا ممکن است به دلیل مسائل پیچیده تر مربوط به کنترل دسترسی در SELinux باشد.

در لاگ خطای nginx می توانید خطی مانند زیر را مشاهده کنید:

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"

اگر Gunicorn را مستقیماً تست کنیم، یک پاسخ خالی می گیریم.

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

بیایید بفهمیم که چرا این اتفاق می افتد. اگر لاگ را باز کنید به احتمال زیاد می بینید که مشکل مربوط به SELinux است. از آنجایی که ما یک دیمون را اجرا می کنیم که هیچ خط مشی برای آن ایجاد نشده است، به عنوان init_t علامت گذاری شده است. بیایید این نظریه را در عمل آزمایش کنیم.

sudo setenforce 0

همه اینها ممکن است باعث انتقاد و اشک خون شود، اما این فقط اشکال زدایی نمونه اولیه است. بیایید چک را غیرفعال کنیم تا مطمئن شویم این مشکل است، پس از آن همه چیز را به جای خود برمی گردانیم.

با رفرش کردن صفحه در مرورگر یا اجرای مجدد دستور curl ما، می توانید صفحه آزمایش جنگو را ببینید.

بنابراین، با اطمینان از اینکه همه چیز کار می کند و دیگر مشکل مجوز وجود ندارد، SELinux را دوباره فعال می کنیم.

sudo setenforce 1

من در اینجا در مورد audit2allow یا ایجاد خط‌مشی‌های مبتنی بر هشدار با sepolgen صحبت نمی‌کنم، زیرا در حال حاضر برنامه واقعی جنگو وجود ندارد، بنابراین نقشه کاملی از آنچه Gunicorn می‌خواهد به آن دسترسی داشته باشد و چه چیزی باید دسترسی به آن را رد کند وجود ندارد. بنابراین، لازم است SELinux را برای محافظت از سیستم در حال اجرا نگه دارید و در عین حال به برنامه اجازه داده شود تا اجرا شود و پیام هایی را در گزارش حسابرسی بگذارد تا سیاست واقعی از آنها ایجاد شود.

تعیین دامنه های مجاز

همه در مورد دامنه های مجاز در SELinux چیزی نشنیده اند، اما چیز جدیدی نیستند. بسیاری حتی بدون اینکه متوجه باشند با آنها کار کردند. هنگامی که یک خط مشی بر اساس پیام های ممیزی ایجاد می شود، خط مشی ایجاد شده نشان دهنده دامنه حل شده است. بیایید سعی کنیم یک خط مشی مجوز ساده ایجاد کنیم.

برای ایجاد یک دامنه مجاز خاص برای Gunicorn، به نوعی خط مشی نیاز دارید و همچنین باید فایل های مناسب را علامت گذاری کنید. علاوه بر این، ابزارهایی برای جمع آوری سیاست های جدید مورد نیاز است.

sudo yum install selinux-policy-devel

مکانیسم دامنه‌های مجاز ابزاری عالی برای شناسایی مشکلات است، به‌ویژه وقتی صحبت از یک برنامه کاربردی سفارشی یا برنامه‌هایی باشد که بدون خط‌مشی‌هایی از قبل ایجاد شده ارسال می‌شوند. در این مورد، خط مشی دامنه مجاز برای Gunicorn تا حد امکان ساده خواهد بود - یک نوع اصلی (gunicorn_t) را اعلام کنید، یک نوع را که برای علامت گذاری چندین فایل اجرایی استفاده خواهیم کرد (gunicorn_exec_t) و سپس یک انتقال برای سیستم تنظیم کنید تا به درستی علامت گذاری شود. فرآیندهای در حال اجرا خط آخر خط مشی را به صورت پیش فرض در زمان بارگیری فعال می کند.

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;

شما می توانید این فایل سیاست را کامپایل کرده و به سیستم خود اضافه کنید.

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

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

بیایید بررسی کنیم که آیا SELinux چیز دیگری غیر از آنچه دیمون ناشناخته ما به آن دسترسی دارد را مسدود می کند یا خیر.

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 از نوشتن داده ها در سوکت یونیکس توسط Gunicorn جلوگیری می کند. به طور معمول، در چنین مواردی، سیاست ها شروع به تغییر می کنند، اما چالش های دیگری در پیش است. همچنین می توانید تنظیمات دامنه را از دامنه محدود به دامنه مجوز تغییر دهید. حالا بیایید httpd_t را به دامنه مجوزها منتقل کنیم. این به Nginx دسترسی لازم را می‌دهد و می‌توانیم به کار اشکال‌زدایی بیشتر ادامه دهیم.

sudo semanage permissive -a httpd_t

بنابراین، هنگامی که توانستید SELinux را محافظت کنید (واقعاً نباید پروژه SELinux را در حالت محدود رها کنید) و دامنه های مجوز بارگیری شدند، باید بفهمید که دقیقاً چه چیزی باید به عنوان gunicorn_exec_t علامت گذاری شود تا همه چیز به درستی کار کند. از نو. بیایید سعی کنیم از وب سایت بازدید کنیم تا پیام های جدید در مورد محدودیت های دسترسی را ببینیم.

sudo ausearch -m AVC -c gunicorn

پیام‌های زیادی حاوی «comm="gunicorn» خواهید دید که کارهای مختلفی را روی فایل‌ها در /srv/djangoapp انجام می‌دهند، بنابراین واضح است که این یکی از دستوراتی است که ارزش پرچم‌گذاری را دارد.

اما علاوه بر این، پیامی مانند این ظاهر می شود:

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

اگر به وضعیت سرویس gunicorn نگاه کنید یا دستور ps را اجرا کنید، هیچ فرآیند در حال اجرا را مشاهده نخواهید کرد. به نظر می رسد که گانیکورن در حال تلاش برای دسترسی به مفسر پایتون در محیط virtualenv ما است، احتمالاً برای اجرای اسکریپت های کارگر. پس حالا بیایید این دو فایل اجرایی را علامت گذاری کنیم و بررسی کنیم که آیا می توانیم صفحه آزمایش جنگو خود را باز کنیم.

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

قبل از انتخاب تگ جدید، سرویس guncorn باید راه اندازی مجدد شود. می‌توانید فوراً آن را مجدداً راه‌اندازی کنید یا سرویس را متوقف کنید و هنگامی که سایت را در مرورگر باز می‌کنید، اجازه دهید سوکت آن را راه‌اندازی کند. بررسی کنید که فرآیندها برچسب های صحیح را با استفاده از ps دریافت کرده باشند.

ps -efZ | grep gunicorn

فراموش نکنید که بعداً یک خط مشی معمولی SELinux ایجاد کنید!

اگر اکنون به پیام‌های AVC نگاه کنید، آخرین پیام حاوی permissive=1 برای هر چیزی که مربوط به برنامه است و permissive=0 برای بقیه سیستم است. اگر درک کنید که یک برنامه واقعی به چه نوع دسترسی نیاز دارد، می توانید به سرعت بهترین راه را برای حل چنین مشکلاتی پیدا کنید. اما تا آن زمان، بهتر است سیستم را ایمن نگه دارید و یک ممیزی واضح و قابل استفاده از پروژه جنگو دریافت کنید.

sudo ausearch -m AVC

اتفاق افتاد!

یک پروژه جنگو در حال کار با یک فرانت اند مبتنی بر Nginx و Gunicorn WSGI ظاهر شده است. ما Python 3 و PostgreSQL 10 را از مخازن RHEL 8 Beta پیکربندی کردیم. اکنون می‌توانید به جلو حرکت کنید و برنامه‌های جنگو را ایجاد کنید (یا به سادگی استقرار دهید) یا ابزارهای موجود در RHEL 8 بتا را برای خودکار کردن فرآیند پیکربندی، بهبود عملکرد یا حتی کانتینری کردن این پیکربندی کاوش کنید.

منبع: www.habr.com

اضافه کردن نظر