RHEL 8 Beta Workshop:アクティブなWebアプリケーションの構築

RHEL 8 Betaは開発者に多くの新機能を提供しますが、そのリストにはページが含まれる場合がありますが、実際に新しいことを学ぶ方が常に良いので、Red Hat Enterprise Linux 8 Betaに基づいてアプリケーションインフラストラクチャを実際に作成するワークショップを経験することをお勧めします。







DjangoとPostgreSQLの組み合わせであるPython(アプリケーションを作成するためのかなり一般的なバンドル)を取り上げ、RHEL 8 Betaがそれらと連携するように構成しましょう。 次に、さらに2つ(未分類)の材料を追加します。



テスト環境は変わります。自動化機能を研究し、コンテナを操作し、複数のサーバーがある環境を試すことが興味深いからです。 新しいプロジェクトで作業を開始するには、小さなシンプルなプロトタイプを手動で作成することから始めます-この方法で、正確に何が起こる必要があり、どのように相互作用が実行されるかを確認し、自動化に進み、より複雑な構成を作成できます。 今日はそのようなプロトタイプを作成する物語です。



RHEL 8 Beta VM仮想マシンイメージを展開することから始めましょう。 仮想マシンを最初からインストールするか、ベータサブスクリプションで利用可能なKVMゲストイメージを使用できます。 ゲストイメージを使用する場合は、クラウド初期化(cloud-init)のメタデータとユーザーデータを含む仮想CDを構成する必要があります。 ディスクや利用可能なパッケージの構造について特別なことをする必要はありません;どんな設定でもかまいません。



プロセス全体をさらに詳しく見てみましょう。



Djangoのインストール



Djangoの最新バージョンでは、Python 3.5以降の仮想環境(virtualenv)が必要です。 ベータ版のメモでは、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 3への切り替えを検討していますが、Python 3自体は活発に開発中であり、新しいバージョンが次々と登場しています。 したがって、安定したシステムツールのニーズを満たすと同時に、さまざまな新しいバージョンのPythonへのアクセスをユーザーに提供するために、システムPythonは新しいパッケージに移行され、Python 2.7と3.6の両方をインストールする機能が提供されました。 変更の詳細と、これが行われた理由については、 Langdon Whiteのブログ投稿を参照してください



したがって、Pythonを機能させるには、2つのパッケージのみをインストールする必要がありますが、python3-pipは依存関係としてプルアップします。



 sudo yum install python36 python3-virtualenv
      
      





Langdonが示唆するように、pip3をインストールせずに、直接モジュール呼び出しを使用しないのはなぜですか? pipモジュールはカスタムpip実行可能ファイルを含む仮想環境(virtualenvs)をサポートしないため、今後の自動化を念頭に置いて、Ansibleがインストールされたpipを必要とすることが知られています。



動作するpython3インタープリターを自由に使用して、Djangoのインストールプロセスを続行し、他のコンポーネントとともに動作するシステムを取得できます。 ネットワークには、実装のための多くのオプションがあります。 ここには1つのバージョンが示されていますが、ユーザーは独自のプロセスを使用できます。



RHEL 8で利用可能なPostgreSQLおよびNginxのバージョンは、Yumを使用してデフォルトでインストールされます。



 sudo yum install nginx postgresql-server
      
      





PostgreSQLはpsycopg2を必要としますが、virtualenv環境でのみ使用可能にする必要があるため、DjangoおよびGunicornとともにpip3を使用してインストールします。 ただし、最初にvirtualenvを設定する必要があります。



Djangoプロジェクトをインストールする適切な場所を選択することについては、常に多くの議論がありますが、疑わしい場合は、いつでもLinux Filesystem Hierarchy Standardに目を向けることができます。 特に、FHSは/ srvを使用して次のことを行うと述べています。「ホスト固有のデータを保存する-システムが提供するデータ、たとえば、Webサーバーからのデータとスクリプト、FTPサーバーに保存されるデータ、および制御システムのリポジトリバージョン(2004年にFHS-2.3で導入)。



これは私たちのケースに過ぎないため、必要なものはすべて、アプリケーションユーザー(クラウドユーザー)が所有する/ 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を初めてインストールするときに留意すべき点が1つあります。postgresql-setupパッケージはpostgresql-serverパッケージと共にインストールされます。 このスクリプトは、クラスターの初期化やプロセスの更新など、データベースクラスターの管理に関連する基本的なタスクの実行に役立ちます。 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)の適切なサーバーとプロキシを構成する必要があります。 一般的なバンドルがいくつかあります。たとえば、uWSGIを使用したApache HTTPDやGunicornを使用したNginxなどです。



Webサーバーゲートウェイインターフェイスの目標は、WebサーバーからPython Webフレームワークにリクエストをリダイレクトすることです。 WSGIは、CGIメカニズムが使用されていたひどい過去の一種の遺産であり、今日のWSGIは、使用されているWebサーバーまたはPythonフレームワークに関係なく、実際には標準です。 しかし、その幅広い分布にもかかわらず、これらのフレームワークを使用する際にはまだ多くの微妙な違いがあり、多くの選択肢があります。 この場合、ソケットを介してGunicornとNginxの相互作用を確立しようとします。



これらのコンポーネントは両方とも同じサーバーにインストールされているため、ネットワークソケットではなくUNIXソケットを使用しようとします。 とにかく通信にはソケットが必要なので、もう1つの手順を試して、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)をマークするために使用するタイプを宣言し、実行中のプロセスを正しくマークするようにシステムの移行を構成します。 最後の行では、ロード時にポリシーがデフォルトで有効に設定されています。



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は、NginxがGunicornが使用するUNIXソケットにデータを書き込むことを防ぎます。 通常、そのような場合、政治家は変化し始めますが、先に他のタスクがあります。 ドメインの設定を変更して、制限ドメインから許可ドメインに変更することもできます。 次に、httpd_tを許可ドメインに移動します。 これにより、Nginxに必要なアクセス権が提供され、デバッグの作業を続行できます。



 sudo semanage permissive -a httpd_t
      
      





したがって、SELinux保護を保持することが可能で(実際、プロジェクトを制限モードでSELinuxのままにしないでください)、許可ドメインがロードされている場合、gunicorn_exec_tとしてマークする必要があるものを正確に見つける必要があります。 Webサイトにアクセスして、アクセス制限に関する新しいメッセージを確認してみましょう。



 sudo ausearch -m AVC -c gunicorn
      
      





/ srv / djangoapp内のファイルに対してさまざまなアクションを実行する 'comm =“ gunicorn”'を含む多くのメッセージを見ることができるため、明らかにこれはチェックする価値のあるコマンドの1つにすぎません。



しかし、さらに、次のようなメッセージが表示されます。



 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インタープリターにアクセスし、おそらくは作業スクリプト(ワーカー)を実行しようとしているようです。 それでは、これら2つの実行可能ファイルにマークを付けて、テスト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
      
      





わかった!



NginxとGunicorn WSGIのフロントエンドを使用したDjangoプロジェクトが登場しました。 RHEL 8 BetaリポジトリからPython 3およびPostgreSQL 10を構成しました。 これで、Djangoアプリケーションに移動して作成(または単にデプロイ)したり、RHEL 8 Betaで利用可能な他のツールを調べて、チューニングプロセスを自動化したり、パフォーマンスを改善したり、この構成をコンテナー化したりすることができます。



All Articles