Python3 Bottleフレームワーク入門(その10)- WSGI via uwsgi Server on SSL
2018年5月27日Python Bottle Framework, トピックス, ノウハウ
今回は、Pythonでお馴染みのWSGI(外部サーバ)の技術を使ったSSL接続を試してみます。
WSGIとはシンプルすぎて非常にわかりにくいものですが、簡単に言うと通常コマンドラインで標準出力しているものを内部及び外部のhttpサーバで使われているネットワークの土管にバイパスしてブラウザへの表示を拝借してしまおうという発想です。
bottleフレームワークはもとからWSGIの内部WEBサーバを走らせる機能が標準装備しているため外部WEBサーバ使う理由に何が必要なのか?という疑問を持つ人もいると思います。それはbottleの標準WEBサーバはシングルスレッドなので多くの接続を処理したり、SSL接続をしたい場合はこのようにWSGIの外部サーバ連携する技術を駆使しないと現実的なWEBサーバとして利用できないのです。ではまず最初にbottleで簡単なWEBサーバを起動してみましょう。
1 2 3 4 5 6 7 8 9 |
from bottle import route, run, template,default_app from bottle import TEMPLATE_PATH @route('/') def index(): return "Hello World! Bottle Simple Web Server!" if __name__ == '__main__': run(host='0.0.0.0', port=80) |
index.pyを実行するとシンプルなWEBサーバ(正確に言うとシングルスレッド内部WSGIサーバです。)が起動します。
1 |
# python index.py |
ここまでは誰でも普段からやっていると思います。
今度はこれを外部接続WSGI仕様に変えてソースを改良します。
特にWSGI関連ライブラリーを呼び出す必要はありません。ポイントはapplicationというクラスです。uWSGIから呼び出すため、mainは生成されずelseへ分岐します。ここへbottleのインスタンスを渡してあげます。このケースではdefault_app()ですが、もしご自分でbottleのインスタンスを他の変数やクラスへ継承させているならそれを代入しましょう。これがWSGI仕様にする要になります。
1 2 3 4 5 6 7 8 9 10 11 |
from bottle import route, run, template,default_app from bottle import TEMPLATE_PATH @route('/') def index(): return "Hello World! Bottle and uWSGI!" if __name__ == '__main__': run(host='0.0.0.0', port=80) else: application = default_app() |
最下行にたった2行が追加されました。たったこれだけです。大変シンプルですね。
次はuwsgiというシンプルな外部WSGI接続が可能なWEBサーバーをpipでインストールします。
1 2 |
# pip3 install uwsgi # uwsgi --http 0.0.0.0:8080 --wsgi-file index.py |
次にwsgiでSSL接続を行ってみましょう。pythonのプログラムは特に修正する必要はありません。uwsgiのネットワークトンネルを拝借していますから、SSLの処理はuwsgiの機能で行います。
テスト環境でSSLサーバの設置を行う場合のやり方
よくオレオレ証明書を使う方がいますが、今は安いSSL証明書なら年間ライセンス1000円程度で購入できます。私はテスト用に1000円のSSLライセンスを購入して実験利用しています。
テスト環境で実験する場合はWindows側では”c:\Windows\System32\Drivers\etc\hosts”を管理者権限モードでテキストエディタで開いてDNSの照合よりも先にhostsファイルに登録したドメインとIPアドレスを参照するように予めテスト用のドメインを登録しておきます。ドメイン認証だけのSSLならこれでうまくいきます。それ以外の認証が入っている高価なSSLだと「安全ではない」がでるかもしれません。
こうすることで、SSL証明書のドメインとテスト用のサーバのドメインを一致させブラウザを騙します。そうしないとhttps接続時に赤色でバツや三角がブラウザ上のURLそばのアイコンが表示されます。
証明書の準備と取得証明書のマージ処理からサーバ起動まで
証明書が購入できたら次に作業するフォルダーに証明書用のフォルダーを作成します。今はとりあえずauthという名前のフォルダーを作成します。そこに購入した証明書ファイルを配置します。以下のような最低でも3種類のファイルが必要となります。
- サーバー証明書(server.crt)
- サーバ秘密鍵(server.key)
- 中間証明書(ca-bundle.ca)
これらのファイルで中間証明書はuwsgiコマンドのパラメータで指定する事ができないため、中間証明書とサーバ証明書は1つのファイルにマージします。
1 |
# cat server.crt ca-bundle.ca > integrated.crt |
マージされた証明書は必ず一度エディターで中を開けてチェックして下さい。順番は関係あります。サーバ証明書が一番上に来るようにして下さい。また中間証明書はサーバ証明書のend区切りコメントに連続にならないように改行を必ず入れてください。そうしないとエラーになります。
下記は証明書を結合した場合の証明書同士のボーダーを示しています。予め結合する前にサーバ証明書に改行を入れておくと安全です。必ず結合後はチェックしましょう。
次のようにuwsgiを起動する際に鍵ファイルや証明書を指定します。
1 |
# uwsgi --master --https 0.0.0.0:443,auth/integrated.crt,auth/server.key,HIGH --wsgi-file index.py |
実際の実行時のターミナル画面は次のようになります。
実際に本番稼働させる場合は実行用のユーザとグループを用意してchrootさせて実行すると以下の警告はなくなります。
その他uwsgiの実行オプションはここパラメータでスレッド数、プロセス数やパフォーマンスに関するその他の項目、ログの場所など細かくチューニングが可能です
スレッド数やプロセス数を増やせば必ずしもマルチスレッドに対応したコンテンツになるとは限りません。例えばコンテンツがREAD主体の静的コンテンツはスレッド数やプロセス数を増やせばマルチスレッドになると思いますが、動的に変化するコンテンツは内部側でもマルチスレッド対応(thread_safeは必須)にしないと駄目なケースが多いと思います。というか、多くのケースはPythonコンテンツ側もマルチスレッド動作に対応する必然性があるかもしれません。マルチスレッド対応とは2種類の意味があります。DBなどファイル書き込みが複数のスレッドで競合が起きないようにするという意味と複数のコネクションを1プロセスで捌くという意味です。今回の場合はスクリプト起動なので必要最低限で前者が必須のケースです。マルチスレッド通信はProxyタイプ通信で連携する場合では必須なので他の章を参考にしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
[root@localhost wsgi]# uwsgi --master --https 0.0.0.0:443,auth/server.crt,auth/server.key,HIGH --wsgi-file index.py *** Starting uWSGI 2.0.17 (64bit) on [Sun May 27 19:50:58 2018] *** compiled with version: 4.8.5 20150623 (Red Hat 4.8.5-28) on 27 May 2018 07:45:36 os: Linux-3.10.0-862.2.3.el7.x86_64 #1 SMP Wed May 9 18:05:47 UTC 2018 nodename: localhost.localdomain machine: x86_64 clock source: unix pcre jit disabled detected number of CPU cores: 1 current working directory: /root/works/wsgi detected binary path: /usr/local/bin/uwsgi uWSGI running as root, you can use --uid/--gid/--chroot options *** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** your processes number limit is 7232 your memory page size is 4096 bytes detected max file descriptor number: 1024 lock engine: pthread robust mutexes thunder lock: disabled (you can enable it with --thunder-lock) uWSGI http bound on 0.0.0.0:443 fd 4 uwsgi socket 0 bound to TCP address 127.0.0.1:45638 (port auto-assigned) fd 3 uWSGI running as root, you can use --uid/--gid/--chroot options *** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** Python version: 3.5.2 (default, May 26 2018, 18:31:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] *** Python threads support is disabled. You can enable it with --enable-threads *** Python main interpreter initialized at 0x1df5120 uWSGI running as root, you can use --uid/--gid/--chroot options *** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** your server socket listen backlog is limited to 100 connections your mercy for graceful operations on workers is 60 seconds mapped 145840 bytes (142 KB) for 1 cores *** Operational MODE: single process *** WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x1df5120 pid: 27627 (default app) uWSGI running as root, you can use --uid/--gid/--chroot options *** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** *** uWSGI is running in multiple interpreter mode *** spawned uWSGI master process (pid: 27627) spawned uWSGI worker 1 (pid: 27628, cores: 1) spawned uWSGI http 1 (pid: 27629) |
ブラウザから以下のようにアクセスができたら成功です。中間証明書がきちんと登録ができていないと今のブラウザは厳しいので「このサイトは安全でない」と表示されるので要注意です。必ず中間証明書とサーバー証明書をマージして区切り線が前後に改行されていることを確認しましょう。
- Python Bottle Framework入門 全13回
- 1.基礎編サーバ起動
- 2.リクエストメソッド
- 3.ORM Peewee (MySQL)
- 4.ORM Peewee CRUD
- 5.Cookie And Session
- 6.Abort and Redirect
- 7.マルチスレッドWEBサーバ
- 8.デーモン化
- 9.Json
- 10.WSGI on SSL
- 11.Apache連携起動(外部WSGI) SSL接続
- 12.Apache連携起動(ReverseProxy)SSL接続
- 13.hprox連携起動(ReverseProxy)SSL接続&HTTP2対応