nginx

Nginx を利用して同一ポートの待ち受けで複数のプロトコルを扱う

転送先毎にサブドメインを用意しておき、TLS の SNI 拡張と Nginx の stream_ssl_module モジュールを利用して、SNI 拡張のドメイン名で転送先を切り替えます。

Nginx

次の設定では、外からのアクセスは 443 ポートでのみ待ち受けていますが、www1.example.com で接続してきた場合は HTTP サーバ(8080)へ、www2.example.com で接続してきた場合は MQTT ブローカー(1883)へ転送しています。

ngixn.conf

http {
    server {
        listen       127.0.0.1:8080;
        server_name  www1.example.com;

        location / {
            root html;
        }
    }
}

stream {
        upstream www1.example.com {
           server 127.0.0.1:8080;
        }
        upstream www2.example.com {
           server 127.0.0.1:1883;
        }
        server {
                listen 443 ssl;
                proxy_pass $ssl_server_name;

                ssl_certificate      /home/ubuntu/certs/server.pem;
                ssl_certificate_key  /home/ubuntu/certs/server.key;

                ssl_protocols TLSv1.2;
        }
}

証明書

SANs に www1.example.com と www2.example.com を指定した証明書を用意します。

$ openssl x509 -noout -text -in server.pem | grep -A1 'X509v3 Subject Alternative Name'
            X509v3 Subject Alternative Name:
                DNS:www1.example.com, DNS:www2.example.com

動作の確認

HTTP (www1.example.com)

$ curl -s -D - -o /dev/null -X GET --tlsv1.2 --cacert ca.pem https://www1.example.com
HTTP/1.1 200 OK
Server: nginx/1.13.7
Date: Fri, 19 Jan 2018 17:13:29 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 19 Jan 2018 15:34:24 GMT
Connection: keep-alive
ETag: "5a621000-264"
Accept-Ranges: bytes

MQTT (www2.example.com)

paho-mqtt で subscriber と publisher を用意して確認します。

  • 最初に subscriber.py を実行しておき、別途 publisher.py を実行
(vs) $ python subscriber.py
b'Hello, World'
  • subscriber
import ssl
import paho.mqtt.client as mqtt

def on_message(client, userdata, msg):
    print(str(msg.payload))

client = mqtt.Client()
client.tls_set("ca.pem", cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2)
client.on_message = on_message
client.connect("www2.example.com", 443, 60)
client.subscribe("a/b")

client.loop_forever()
  • publisher
import ssl
import paho.mqtt.client as mqtt

client = mqtt.Client()
client.tls_set("ca.pem", cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2)
client.connect("www2.example.com", 443, 60)
client.publish("a/b", "Hello, World")