RHEL 8 Beta 研討會:建立工作 Web 應用程式

RHEL 8 Beta 為開發人員提供了許多新功能,這些功能的清單可能需要幾頁紙,但是,在實踐中學習新東西總是更好,因此下面我們提供了一個關於實際創建基於Red Hat Enterprise Linux 8 Beta 的應用程式基礎架構的研討會。

RHEL 8 Beta 研討會:建立工作 Web 應用程式

讓我們以 Python(一種在開發人員中流行的程式語言)為基礎,結合 Django 和 PostgreSQL(一種用於創建應用程式的相當常見的組合),並配置 RHEL 8 Beta 來使用它們。 然後我們將添加更多(未分類)成分。

測試環境將會改變,因為探索自動化的可能性、使用容器以及嘗試具有多個伺服器的環境是很有趣的。 要開始新項目,您可以先手動建立一個小型、簡單的原型,這樣您就可以準確地看到需要發生什麼以及它如何交互,然後繼續自動化並創建更複雜的配置。 今天我們談論的是這樣一個原型的創建。

我們首先部署 RHEL 8 Beta VM 映像。 您可以從頭開始安裝虛擬機,或使用 Beta 訂閱提供的 KVM 來賓映像。 使用來賓映像時,您需要設定一個虛擬 CD,其中包含用於雲端初始化 (cloud-init) 的元資料和使用者資料。 您無需對磁碟結構或可用軟體包進行任何特殊操作;任何配置都可以。

讓我們仔細看看整個過程。

安裝Django

使用最新版本的 Django,您將需要一個包含 Python 3.5 或更高版本的虛擬環境 (virtualenv)。 在 Beta 註解中,您可以看到 Python 3.6 可用,讓我們檢查一下是否確實如此:

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

Red Hat在RHEL中積極使用Python作為系統工具包,那麼為什麼會出現這樣的結果呢?

事實是,許多Python開發者仍在考慮從Python 2過渡到Python 2,而Python 3本身正在積極開發中,並且越來越多的新版本不斷出現。 因此,為了滿足對穩定係統工具的需求,同時讓使用者能夠存取各種新版本的Python,系統Python被移入新的套件中,並提供了安裝P​​ython 2.7和3.6的能力。 有關這些更改及其原因的更多信息,請參閱以下出版物: 蘭登懷特的博客 (蘭登懷特)。

因此,要使用 Python,您只需要安裝兩個套件,其中包含 python3-pip 作為依賴項。

sudo yum install python36 python3-virtualenv

為什麼不按照 Langdon 建議使用直接模組呼叫並安裝 pip3? 請記住即將到來的自動化,眾所周知,Ansible 將需要安裝 pip 才能運行,因為 pip 模組不支援具有自訂 pip 可執行檔的 virtualenvs。

有了可用的 python3 解釋器,您就可以繼續 Django 安裝過程,並擁有一個工作系統以及我們的其他元件。 互聯網上有許多可用的實施選項。 這裡提供了一種版本,但使用者可以使用自己的流程。

預設情況下,我們將使用 Yum 安裝 RHEL 8 中可用的 PostgreSQL 和 Nginx 版本。

sudo yum install nginx postgresql-server

PostgreSQL 將需要 psycopg2,但它只需要在 virtualenv 環境中可用,因此我們將使用 pip3 以及 Django 和 Gunicorn 來安裝它。 但首先我們需要設定 virtualenv。

關於選擇安裝 Django 專案的正確位置的話題始終存在很多爭論,但是當有疑問時,您可以隨時轉向 Linux 檔案系統層次結構標準。 具體來說,FHS 表示/srv 用於:「儲存特定於主機的資料-系統產生的數據,例如Web 伺服器資料和腳本、儲存在FTP 伺服器上的資料以及控制系統儲存庫。」版本(出現在在 FHS 中)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-server 套件一起安裝的 postgresql-setup 腳本。 此腳本可協助您執行與資料庫叢集管理相關的基本任務,例如叢集初始化或升級流程。 要在 RHEL 系統上設定新的 PostgreSQL 實例,我們需要執行下列命令:

sudo /usr/bin/postgresql-setup -initdb

然後,您可以使用 systemd 啟動 PostgreSQL、建立資料庫並在 Django 中設定專案。 請記住在更改客戶端身份驗證設定檔(通常是 pg_hba.conf)以配置應用程式使用者的密碼儲存後重新啟動 PostgreSQL。 如果遇到其他困難,請確保更改 pg_hba.conf 檔案中的 IPv4 和 IPv6 設定。

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? 威?

開發伺服器對於測試很有用,但要運行應用程序,您必須為 Web 伺服器網關介面 (WSGI) 配置適當的伺服器和代理程式。 有幾種常見的組合,例如 Apache HTTPD 與 uWSGI 或 Nginx 與 Gunicorn。

Web 伺服器閘道介面的工作是將來自 Web 伺服器的請求轉送到 Python Web 框架。 WSGI 是 CGI 引擎出現時可怕的過去的遺跡,而如今 WSGI 已成為事實上的標準,無論使用什麼 Web 伺服器或 Python 框架。 但是,儘管它被廣泛使用,但在使用這些框架時仍然存在許多細微差別和許多選擇。 在這種情況下,我們將嘗試透過套接字在 Gunicorn 和 Nginx 之間建立交互作用。

由於這兩個元件都安裝在同一台伺服器上,因此我們嘗試使用 UNIX 套接字而不是網路套接字。 由於通訊在任何情況下都需要套接字,因此我們嘗試再採取一步,透過 systemd 為 Gunicorn 配置套接字啟動。

建立套接字啟動服務的過程非常簡單。 首先,建立一個單元文件,其中包含指向將建立 UNIX 套接字的點的 ListenStream 指令,然後建立一個服務單元文件,其中 Requires 指令將指向套接字單元文件。 然後,在服務單元檔案中,剩下的就是從虛擬環境呼叫 Gunicorn 並為 UNIX 套接字和 Django 應用程式建立 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;
   }
}

使用 systemd 啟動 Gunicorn 套接字和 Nginx,然後就可以開始測試了。

網關錯誤?

如果您在瀏覽器中輸入該位址,您很可能會收到 502 Bad Gateway 錯誤。 它可能是由於錯誤配置的 UNIX 套接字權限引起的,也可能是由於與 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命令,您可以看到Django測試頁面。

因此,在確保一切正常並且不再存在權限問題後,我們再次啟用 SELinux。

sudo setenforce 1

我不會在這裡談論audit2allow或使用sepolgen創建基於警報的策略,因為目前沒有實際的Django應用程序,因此沒有Gunicorn可能想要訪問的內容以及應該拒絕訪問的內容的完整映射。 因此,有必要保持 SELinux 運行以保護系統,同時允許應用程式運行並在審核日誌中留下訊息,以便可以根據它們建立實際的策略。

指定許可域

並不是每個人都聽說過 SELinux 中允許的領域,但它們並不是什麼新鮮事。 許多人甚至在沒有意識到的情況下與他們合作。 當基於審核訊息建立策略時,建立的策略代表已解析的網域。 讓我們嘗試建立一個簡單的許可策略。

要為 Gunicorn 建立特定的允許網域,您需要某種策略,並且還需要標記適當的檔案。 此外,還需要工具來制定新政策。

sudo yum install selinux-policy-devel

允許的域機制是識別問題的一個很好的工具,特別是當涉及自訂應用程式或未建立策略的應用程式時。 在這種情況下,Gunicorn 允許的域策略將盡可能簡單- 聲明一個主類型(gunicorn_t),聲明一個我們將用於標記多個可執行檔的類型(gunicorn_exec_t),然後為系統設定轉換以正確標記正在運行的進程。 最後一行將策略設定為在載入時預設啟用。

古尼康.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 阻止 Nginx 將資料寫入 Gunicorn 使用的 UNIX 套接字。 通常,在這種情況下,政策會開始改變,但未來還會面臨其他挑戰。 您也可以將網域設定從限制域變更為權限域。 現在讓我們將 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指令,您將看不到任何正在執行的進程。 看起來 Gunicorn 正在嘗試存取 virtualenv 環境中的 Python 解譯器,可能是為了執行工作腳本。 現在讓我們標記這兩個可執行檔並檢查是否可以開啟 Django 測試頁面。

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

在選擇新標籤之前,需要重新啟動gunicorn 服務。 您可以立即重新啟動它,也可以停止服務並讓套接字在您在瀏覽器中開啟網站時啟動它。 使用 ps 驗證進程是否已收到正確的標籤。

ps -efZ | grep gunicorn

不要忘記稍後創建一個正常的 SELinux 策略!

如果您現在查看 AVC 訊息,最後一則訊息包含與應用程式相關的所有內容的 permissive=1,以及針對系統其餘部分的 permissive=0。 如果您了解實際應用程式需要什麼樣的訪問,您可以快速找到解決此類問題的最佳方法。 但在此之前,最好保持系統安全並對 Django 專案進行清晰、可用的審核。

sudo ausearch -m AVC

發生了!

一個可用的 Django 專案已經出現,其前端基於 Nginx 和 Gunicorn WSGI。 我們從 RHEL 3 Beta 儲存庫配置了 Python 10 和 PostgreSQL 8。 現在,您可以繼續建立(或簡單部署)Django 應用程序,或探索 RHEL 8 Beta 中的其他可用工具,以自動化配置流程、提高效能,甚至容器化此配置。

來源: www.habr.com

添加評論