検索ガイド -Search Guide-

単語と単語を空白で区切ることで AND 検索になります。
例: python デコレータ ('python' と 'デコレータ' 両方を含む記事を検索します)
単語の前に '-' を付けることで NOT 検索になります。
例: python -デコレータ ('python' は含むが 'デコレータ' は含まない記事を検索します)
" (ダブルクオート) で語句を囲むことで 完全一致検索になります。
例: "python data" 実装 ('python data' と '実装' 両方を含む記事を検索します。'python data 実装' の検索とは異なります。)
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
  • ただいまサイドメニューのテスト中/ただいまサイドメニューのテスト中
>>

1つのIPアドレス、1つのドメインを割り当てている Apache サーバーで、複数のホスト名へのリクエストを VirtualHost で振り分け、それぞれに異なる Let's Encrypt の SSL 証明書を割り当てて HTTPS アクセスを可能にしてみた 投稿一覧へ戻る

Published 2022年3月14日9:28 by mootaro23

SUPPORT UKRAINE

- Your indifference to the act of cruelty can thrive rogue nations like Russia -

この記事は順次更新していきます。
気長にお付き合いください。

現状および計画:

・Djangoアプリケーションは以前からこのサーバー上で稼働しており、Let's Encrypt から取得した証明書で SSL 接続が可能となっていました。

・今回、フロントエンド部分を React を利用して構築することを計画し、最終的にはバックエンドの Django は django REST framework を利用した API 提供に特化させようと思っています。

・React アプリケーションを deploy するための新たなサーバーを用意したり、そのための IP アドレスを取得する予定はありません。

・当座は現在のサーバーに Django プロジェクトと React アプリケーションを共存させ、django REST framework と React による開発を進めていく予定です。

・フロントエンドアプリケーションとバックエンドアプリケーションは分離させたいのでそれぞれ別のホスト名でアクセス可能にし、また、それぞれのアクセスを HTTPS スキームで行えるように設定したいと考えています。

・Let's Encrypt を利用する際の ACME クライアントとして Certbot を利用していますが、今までは certbot-auto スクリプトを介して更新等の作業を行っていました。しかし certbot-auto のサポートは終了しているため、これを機会に一度環境をクリアにし、Certbot を一からインストール、それぞれのアプリケーションにアクセスするためのドメインに対する SSL 証明書を取得し直すことにしました。


イメージは以下の通りです:

ip_address_domain_routine

前提:

・AlmaLinux 8 (CentOS 8) 上で web server として apache を動かしています。

・サーバーには1つの IP アドレスが割り当てられています (xxx.xxx.xxx.xxx)。

・Apache の待ち受けポートは 80 番です。

・ドメイン (example.com) も1つだけ取得済みで、DNS の設定で IP アドレスと結びつけています。

・また、DNS の設定で、同じ IP アドレスに対して2つの異なるホスト名も登録してあります (front と back)。

・つまり、xxx.xxx.xxx.xxx には、example.com, front.exmaple.com, back.example.com というドメイン名でアクセス可能です。

・example.com と back.example.com へのアクセスは Django アプリケーションへのアクセスです。

・front.example.com へのアクセスは React アプリケーションへのアクセスです。

・Apache のコンフィギュレーションファイルに2つの <VirtualHost> ブロックを設定し、それぞれの ServerName ディレクティブの値で処理を振り分けています。


certbot-auto スクリプトのアンインストール:

Certbot を一からインストールし直して Let's Encrypt の証明書を取得し直すために、サポートが終了している certbot-auto をアンインストールしました。

手順は Uninstalling certbot-auto の記事を参照に行いました。

1: cron を利用して証明書の自動更新をスケジュールしていたので、設定を行ったのと同じユーザーで、

crontab -e

コマンドを実行してエディタを開き、該当部分を削除しました。


crontab -r

ですべてのスケジュールを削除することもできます。


2: certbot-auto スクリプトを削除します:
sudo rm /usr/local/bin/certbot-auto



3: certbot-auto でインストールされた Certbot を削除します:
sudo rm -rf /opt/eff.org



Apache コンフィギュレーションファイル:

この時点では以下のようになっていました:

LoadModule wsgi_module /path/to/wsgi/module/mod_wsgi.so

NameVirtualHost *:80

WSGIApplicationGroup %{RESOURCE}
WSGIRestrictEmbedded On

<VirtualHost *:80>
ServerName back.example.com
ServerAlias example.com

WSGIDaemonProcess venv python-home=/path/to/virtual/environment/root python-path=/path/to/django/project/root
WSGIProcessGroup venv

WSGIScriptAlias / /path/to/django/project_root/project_name/wsgi.py
<Directory /path/to/django/project_root/project_name>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerName front.example.com

DocumentRoot /path/to/document/root
<Directory /path/to/document/root>
Require all granted
</Directory>
</VirtualHost>



Certbot のインストール:

Let's Encrypt の証明書の取得には ACME クライアントが必要ですが、公式 HP では Certbot が推薦されているため改めて Certbot をインストールします。

Certbot のサイト で、自分が使用しているサーバーソフトウェアと OS を入力するとインストール手順書が表示されますので、その通りに進めていきます。

certbot_instrall_document

私は AlmaLinux 8 を利用していますが選択項目には含まれていなかったため CentOS 8 を選択しました。

1: snap パッケージ管理システムのインストール:

Certbot のインストールには snap パッケージ管理システムの利用が推薦されているため snapd のインストールを行います。

まず、epel リポジトリが利用できるか確かめます:
sudo dnf repolist --enabled



リストに含まれていて「有効化」されていれば大丈夫。

「無効化」されているときは「有効化」します:
sudo dnf config-manager --enable epel



含まれていない場合はインストールします:
sudo dnf config-manager --add-repo epel



epel リポジトリが利用できるようになったらシステムをアップデートします:
sudo dnf upgrade



snapd パッケージをインストールします:
sudo dnf install snapd



snapd.socket サービスを有効にします:
sudo systemctl enable --now snapd.socket



classic snap を有効にするために、シンボリックリンクを張ります:
sudo ln -s /var/lib/snapd/snap /snap



システムをリブートします:
sudo reboot



snap のバージョンが最新になるようにします:
sudo snap install core
sudo snap refresh core



2: Certbot のインストール


Certbot をインストールします:
sudo snap install --classic certbot



Certbot コマンドを実行する際に path が通るようにシンボリックリンクを張ります:
sudo ln -s /snap/bin/certbot /usr/bin/certbot




Let's Encrypt の証明書の取得

以下のコマンドを実行します:

sudo certbot --apache


表示される質問に答えていきます:

Enter email address: メールアドレス入力

Please read the Terms of Service ...: 使用許諾書に同意 → Y

Would you be willing , once your ...: Certbot からの連絡を受け取る場合は → Y

Which names would you like to activate HTTPS for?:


この例でいえば、

1: front.example.com
2: example.com
3: back.example.com


のように表示されます。

何も入力せずに Enter を押すとすべてを選択したことになります。

今回は、Django アプリと React アプリへのドメインを分け、証明書も別々に取得したいので、まず Django アプリで使用するドメインに関して証明書を取得するため、

2,3


と入力します。

これで証明書が発行されますが、警告が表示されます (コンフィギュレーションファイルは前出のものだとします):

1: NameVirtualHost has no effect

2: Name duplicates previous WSGI daemondefinition


1 に関しては無視します。

2 は、Certbot が <VirtualHost *.443> ブロックを自動作成する時点で、該当する <VirtualHost *.80> の内容をコピーするのですが、結果として重複してはいけない WSGIDaemonProcess も含んでしまっているために発生しています。

そこで Certbot が作成した <VirtualHost *:443> ブロックに含まれている WSGIDaemonProcess の行を削除します (Certbot は、'conf_name'-le-ssl.conf というファイルを作成してその中に定義しているようです)。



この時点での conf ファイルの内容:

LoadModule wsgi_module /path/to/wsgi/module/mod_wsgi.so

NameVirtualHost *:80

WSGIApplicationGroup %{RESOURCE}
WSGIRestrictEmbedded On

<VirtualHost *:80>
ServerName back.example.com
ServerAlias example.com

WSGIDaemonProcess venv python-home=/path/to/virtual/environment/root python-path=/path/to/django/project/root
WSGIProcessGroup venv

WSGIScriptAlias / /path/to/django/project_root/project_name/wsgi.py
<Directory /path/to/django/project_root/project_name>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

RewriteEngine on
RewriteCond %{SERVER_NAME} =back.example.com [OR]
RewriteCond %{SERVER_NAME} =example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

<VirtualHost *:80>
ServerName front.example.com

DocumentRoot /path/to/document/root
<Directory /path/to/document/root>
Require all granted
</Directory>
</VirtualHost>

<VirtualHost *:443>
ServerName back.example.com
ServerAlias example.com

WSGIProcessGroup venv

WSGIScriptAlias / /path/to/django/project_root/project_name/wsgi.py
<Directory /path/to/django/project_root/project_name>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
</VirtualHost>



証明書取得対象としたサーバーの <VirtualHost *:80> ディレクティブ内に rewrite 構文が追加され、80 番ポート宛に送られてきた該当ドメインに対するリクエストが、443 番ポートにリダイレクトされるようになっています。

また、該当ドメインの <VirtualHost *:443> ディレクティブ (この内容は同じフォルダ内に作成された 'conf_name'-le-ssl.conf ファイル内に記述されているものと同じです) には証明書までの path が指定されています。


私はこの <VirtualHost *:443> ... </VirtualHost> ディレクティブの内容を Certbot が作成した 'conf_name'-le-ssl.conf ファイルからこのコンフィギュレーションファイルへコピー&ペーストしましたが、この作業が本当に必要なのか、それとも、Certbot が作成した xxx-le-ssl.conf だけが存在すればよいのか、不明です。




この時点で http://back.example.com、https://back.example.com、http://example.com、https://example.com へアクセスすると正常に読み込まれました。

ただし、Django を利用している場合に static files を集めたフォルダへの Alias を設定している場合、その記述を:


1: conf ファイルのグローバル領域ではなく、該当サーバーの <VirtualHost *:443> ディレクティブ内に記述する

2: Certbot が作成した xxx-le-ssl.conf の該当ディレクティブ内にも記述する



の両方、もしくは 2: のみ、の対応が必要なようです。
この記事に興味のある方は次の記事にも関心を持っているようです...
- People who read this article may also be interested in following articles ... -
Let's encrypt を利用してやっと SSL 通信対応になりました
Python coding challenge - 隣り合った庭園には異なる花が植えられているようにするにはどの花を植えればいい?
【Python 雑談・雑学】 ユーザー入力に応じて異なる処理関数を実行する際の少し面白い実装方法 - ユーザーの選択項目とそれに応じる関数名をディクショナリで保持する -
【 Effective Python, 2nd Edition 】__set_name__ デスクリプタ専用特殊関数 ( special method for descriptor ) を利用してデスクリプタインスタンスを割り当てたクラス変数名を取得し、コードの冗長性を排除しよう!
Django の forms は使わずに jQuery で POST / GET リクエスト、レスポンスで template を返してページの一部だけを入れ替えて四択クイズを作ってみた!
Practical Python Design Patterns - Python で学ぶデザインパターン: The Chain of Responsibility Pattern - Part. 3 「WSGI サーバーのセットアップ part 2: Postman を利用した API エンドポイントのテスト」の巻
Practical Python Design Patterns - Python で学ぶデザインパターン: The Chain of Responsibility Pattern - Part. 2 「WSGI サーバーのセットアップ part 1: Windows では Cygwin のインストールが必要な場合があります」の巻