ファイナンス、情報通信技術のスキル・アグリゲーション・サイト

' . iseeit.jp 情報通信技術 . '

Apache 2.4 で Secure WebSocket(wss://)を通す

Apache 2.4 では、mod_proxy_wstunnel モジュールを利用して、WebSocket の接続をバックエンドの WebSocket サーバへ通すことができます。

https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html

WSL(Windows Subsystem for Linux)の Ubuntu 16.04 LTS 上で、Secure WebSocket (wss://)の設定と動作を試してみます。

Secure WebSocket プロクラムは、「Python Tornado で Secure WebSocket」のサンプルプログラムをもとにしました。

また、SSL 証明書は、プライベート証明書を作成しました。手順については、「プライベート認証局でプライベート SSL/TLS 証明書を発行する」も参照してください。

今回のホスト名とプライベートサーバ証明書の Common Name は、wsl.local としています。Bash 上で、ifconfig コマンドから IP アドレスを確認して、/etc/hosts ファイルと、Windows10 の C:¥Windows¥System32¥drivers¥etc¥hosts に IP アドレスとホスト名を設定しています。

ブラウザからの確認ため、プライベート CA 証明書を Internet Explorer にも設定しました。

wss:// を tornado へ転送する

まず、モジュールが有効か確認します。有効にするには、以下のコマンドを実行します。



$ a2enmod proxy
$ a2enmod proxy_http
$ a2enmod proxy_wstunnel

Apache 2.4 における設定方法について調べていると、大きく2通りあるようです。

ひとつは、Proxy による場合で、以下はその設定例です。



<VirtualHost wsl.local:443>
		ServerAdmin webmaster@localhost
		DocumentRoot /var/www/html
		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined
		SSLEngine on
		SSLCertificateFile	/etc/ssl/certs/server.crt
		SSLCertificateKeyFile /etc/ssl/private/server.key
		SSLCACertificatePath /etc/ssl/ca/
		SSLCACertificateFile /etc/ssl/ca/ca.crt
		<FilesMatch "\.(cgi|shtml|phtml|php)$">
				SSLOptions +StdEnvVars
		</FilesMatch>
		<Directory /usr/lib/cgi-bin>
				SSLOptions +StdEnvVars
		</Directory>
		SSLProxyEngine On
		ProxyPreserveHost On
		ProxyPass /ws wss://wsl.local:8888/ws
		ProxyPassReverse /ws wss://wsl.local:8888/ws
</VirtualHost>

もうひとつは、Rewrite による場合です。

以下のコマンドでモジュールを有効にします。



$ a2enmod rewrite

Rewrite による設定例です。



<VirtualHost wsl.local:443>
		ServerAdmin webmaster@localhost
		DocumentRoot /var/www/html
		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined
		SSLEngine on
		SSLCertificateFile	/etc/ssl/certs/server.crt
		SSLCertificateKeyFile /etc/ssl/private/server.key
		SSLCACertificatePath /etc/ssl/ca/
		SSLCACertificateFile /etc/ssl/ca/ca.crt
		<FilesMatch "\.(cgi|shtml|phtml|php)$">
				SSLOptions +StdEnvVars
		</FilesMatch>
		<Directory /usr/lib/cgi-bin>
				SSLOptions +StdEnvVars
		</Directory>
		SSLProxyEngine On
		RewriteEngine On
		RewriteCond %{HTTP:Connection} =Upgrade [NC]
		RewriteCond %{HTTP:Upgrade} =WebSocket [NC]
		RewriteRule /(.*) wss://wsl.local:8888/$1 [P,L]
</VirtualHost>

Tornado を利用した Secure WebSocket サーバ例です。

http://www.tornadoweb.org/en/stable/websocket.html



#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web

class WSHandler(tornado.websocket.WebSocketHandler):
  clients = []
  def check_origin(self, origin):
    return True

  def open(self):
    WSHandler.clients.append(self)
    print "Connected.\n"

  def on_message(self, message):
    print "Received message:%s\n" % message
    for con in WSHandler.clients:
      con.write_message(message)

  def on_close(self):
    WSHandler.clients.remove(self)
    print "Closed.\n"

application = tornado.web.Application([
  (r"/ws", WSHandler),])

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(
    application, ssl_options={
    "certfile": "/etc/ssl/certs/server.crt",
    "keyfile":  "/etc/ssl/private/server.key",})
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

Python の websocket-client モジュールを利用したクライアントプログラムから接続してみます。

https://github.com/websocket-client/websocket-client



#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
import ssl
from websocket import create_connection

ws = create_connection("wss://wsl.local/ws",
  sslopt={"cert_reqs": ssl.CERT_NONE,
          "check_hostname": False})
 
if len(sys.argv) > 1:
  message = sys.argv[1]
else:
  message = "hello websocket!"
 
sndlen = ws.send(message)
rcvmsg = ws.recv()
print "Received:%s\n" % rcvmsg
 
ws.close()

wss:// と https:// を tornado へ転送する

次に、https:// も Proxy によって Tornado へ転送する設定を追加した場合です。

wss:// も、Proxy による場合の設定例です。



<VirtualHost wsl.local:443>
		ServerAdmin webmaster@localhost
		DocumentRoot /var/www/html
		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined
		SSLEngine on
		SSLCertificateFile	/etc/ssl/certs/server.crt
		SSLCertificateKeyFile /etc/ssl/private/server.key
		SSLCACertificatePath /etc/ssl/ca/
		SSLCACertificateFile /etc/ssl/ca/ca.crt
		<FilesMatch "\.(cgi|shtml|phtml|php)$">
				SSLOptions +StdEnvVars
		</FilesMatch>
		<Directory /usr/lib/cgi-bin>
				SSLOptions +StdEnvVars
		</Directory>
		SSLProxyEngine On
		ProxyPreserveHost On
		ProxyPass /ws wss://wsl.local:8888/ws
		ProxyPassReverse /ws wss://wsl.local:8888/ws
		ProxyPass / https://wsl.local:8888/
		ProxyPassReverse / https://wsl.local:8888/
</VirtualHost>

wss:// は、Rewrite による設定例です。



<VirtualHost wsl.local:443>
		ServerAdmin webmaster@localhost
		DocumentRoot /var/www/html
		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined
		SSLEngine on
		SSLCertificateFile	/etc/ssl/certs/server.crt
		SSLCertificateKeyFile /etc/ssl/private/server.key
		SSLCACertificatePath /etc/ssl/ca/
		SSLCACertificateFile /etc/ssl/ca/ca.crt
		<FilesMatch "\.(cgi|shtml|phtml|php)$">
				SSLOptions +StdEnvVars
		</FilesMatch>
		<Directory /usr/lib/cgi-bin>
				SSLOptions +StdEnvVars
		</Directory>
		SSLProxyEngine On
		RewriteEngine On
		RewriteCond %{HTTP:Connection} =Upgrade [NC]
		RewriteCond %{HTTP:Upgrade} =WebSocket [NC]
		RewriteRule /(.*) wss://wsl.local:8888/$1 [P,L]
		ProxyPreserveHost On
		ProxyPass / https://wsl.local:8888/
		ProxyPassReverse / https://wsl.local:8888/
</VirtualHost>

Tornado のテンプレートを利用した例です。HTML5 & JQuery の WebSocket クライアントで、チャットプログラムを簡略にしたようなデザインです。index.html のファイル名で、templates ディレクトリに保存します。templates ディレクトリは、 Python Tornado プログラムファイルを保存したディレクトリ内に作成します。



#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import os

class MainHandler(tornado.web.RequestHandler):
  def get(self):
    self.render("index.html")

class WSHandler(tornado.websocket.WebSocketHandler):
  clients = []
  def check_origin(self, origin):
    return True

  def open(self):
    WSHandler.clients.append(self)
    print "Connected.\n"

  def on_message(self, message):
    print "Received message:%s\n" % message
    for con in WSHandler.clients:
      con.write_message(message)

  def on_close(self):
    WSHandler.clients.remove(self)
    print "Closed.\n"

settings = {
  "template_path": os.path.join(os.path.dirname(__file__), "templates"),
} 

application = tornado.web.Application([
  (r"/ws", WSHandler),
  (r"/", MainHandler),
], **settings)

if __name__ == "__main__":
  http_server = tornado.httpserver.HTTPServer(
    application, ssl_options={
    "certfile": "/etc/ssl/certs/server.crt",
    "keyfile":  "/etc/ssl/private/server.key",})
  http_server.listen(8888)
  tornado.ioloop.IOLoop.instance().start()

テンプレートの index.html です。



<!DOCTYPE html>
<html>
 <head>
  <meta charset="UTF-8">
  <title>Tornado Chat Demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
  <script>
function ws() {
  var data = {};
  $("#chat").prepend("<p>Websocket START</p>");
  var s = new WebSocket("wss://wsl.local/ws");
  s.onerror = function() {
    $("#chat").prepend("<p>ERROR(/ws)</p>");
  }
  s.onopen = function() {
    $("#chat").prepend("<p>OPEN(/ws)</p>");
  }
  s.onmessage = function(e) {
    $("#chat").prepend("<p>" + e.data + "</p>");
  }
  $("#chatform").submit(function (evt) {
    var line = $("#chatform [type=text]").val()
    $("#chatform [type=text]").val("")
    s.send(line);
    return false;
  });
}
  </script>
 </head>
 <body>
  <div id="chat" style="width: 60em; height: 20em; overflow:auto; border: 1px solid black">
  </div>
  <form id="chatform">
  <p>Message</p>
  <input type="text" />
  <input type="submit" value="送信" />
  </form>
  <script>
window.onload = function() {
  $("#chat").prepend("<p>ONLOAD</p>");
  ws();
};
  </script>
 </body>
</html>

ファイナンシャル・プランニング
6つの係数

終価係数 : 元本を一定期間一定利率で複利運用したとき、将来いくら になるかを計算するときに利用します。

現価係数 : 将来の一定期間後に目標のお金を得るために、現在いくら の元本で複利運用を開始すればよいかを計算するときに利用します。

年金終価係数 : 一定期間一定利率で毎年一定金額を複利運用で 積み立て たとき、将来いくら になるかを計算するときに利用します。

年金現価係数 : 元本を一定利率で複利運用しながら、毎年一定金額を一定期間 取り崩し ていくとき、現在いくら の元本で複利運用を開始すればよいかを計算するときに利用します。

減債基金係数 : 将来の一定期間後に目標のお金を得るために、一定利率で一定金額を複利運用で 積み立て るとき、毎年いくら ずつ積み立てればよいかを計算するときに利用します。

資本回収係数 : 元本を一定利率で複利運用しながら、毎年一定金額を一定期間 取り崩し ていくとき、毎年いくら ずつ受け取りができるかを計算するときに利用します。

積み立て&取り崩しモデルプラン

積立金額→年金額の計算 : 年金終価係数、終価係数、資本回収係数を利用して、複利運用で積み立てた資金から、将来取り崩すことのできる年金額を計算します。

年金額→積立金額の計算 : 年金現価係数、現価係数、減債基金係数を利用して、複利運用で将来の年金プランに必要な資金の積立金額を計算します。