EMQ X 內置對 TLS/DTLS 的支持,包括支持單雙向認證、X.509 證書等多種身份認證和 LB Proxy Protocol V1/2 等。你能夠爲 EMQ X 支持的全部協議啓用 TLS/DTLS,也能夠將 EMQ X 提供的 HTTP API 配置爲使用 TLS。本文以自簽證書的方式介紹如何在 EMQ X 中爲 MQTT 啓用 TLS。git
$ docker pull centos:centos7 $ docker run -it --name centos7 centos:centos7 /bin/sh $ yum install openssl $ yum install vim $ mkdir /opt/ssl $ cd /opt/ssl/ $ cp /etc/pki/tls/openssl.cnf ./ $ rm -rf /etc/pki/CA/*.old ## 生成證書索引庫數據庫文件 $ touch /etc/pki/CA/index.txt ## 指定第一個頒發證書的序列號 $ echo 01 > /etc/pki/CA/serial $ rm -rf certs;mkdir certs
$ openssl genrsa -out certs/root-ca.key 2048 $ openssl req -new -x509 -days 365 -config ./openssl.cnf -key certs/root-ca.key -out certs/root-cacert.pem -subj "/C=CN/ST=hangzhou/O=EMQ/CN=RootCA"
查看證書github
$ openssl x509 -in certs/root-cacert.pem -noout -text ... X509v3 Basic Constraints: CA:TRUE ...
$ openssl genrsa -out certs/client.key 2048 $ openssl req -new -days 365 -key certs/client.key -out certs/client-cert.csr -subj "/C=CN/ST=hangzhou/O=EMQ/CN=Client" $ openssl ca -config ./openssl.cnf -extensions v3_req -days 365 -in certs/client-cert.csr -out certs/client-cert.pem -cert certs/root-cacert.pem -keyfile certs/root-ca.key
查看證書docker
$ openssl x509 -in certs/client-cert.pem -noout -text ... X509v3 Basic Constraints: CA:FALSE ...
$ openssl genrsa -out certs/server.key 2048 $ openssl req -new -days 365 -key certs/server.key -out certs/server-cert.csr -subj "/C=CN/ST=hangzhou/O=EMQ/CN=Server" $ openssl ca -config ./openssl.cnf -extensions v3_req -days 365 -in certs/server-cert.csr -out certs/server-cert.pem -cert certs/root-cacert.pem -keyfile certs/root-ca.key
$ openssl verify -CAfile certs/root-cacert.pem certs/server-cert.pem
$ openssl s_server -accept 2009 -key certs/server.key -cert certs/server-cert.pem $ openssl s_client -connect localhost:2009 -CAfile certs/root-cacert.pem -showcerts Verify return code: 0 (ok)
$ openssl s_server -accept 2009 -key certs/server.key -cert certs/server-cert.pem -CAfile certs/root-cacert.pem -Verify 1 $ openssl s_client -connect localhost:2009 -key certs/client.key -cert certs/client-cert.pem -CAfile certs/root-cacert.pem -showcerts Verify return code: 0 (ok)
建立 Root CA 自簽名證書的步驟與前文一致,再也不贅述。shell
$ ls certs root-ca.key root-cacert.pem
$ openssl genrsa -out certs/second-ca.key 2048 $ openssl req -new -days 365 -key certs/second-ca.key -out certs/second-cacert.csr -subj "/C=CN/ST=hangzhou/O=EMQ/CN=SecondCA" $ openssl ca -config ./openssl.cnf -extensions v3_ca -days 365 -in certs/second-cacert.csr -out certs/second-cacert.pem -cert certs/root-cacert.pem -keyfile certs/root-ca.key
與前文相似,只不過須要將 Root CA 的信息替換成 Second CA 的。最後咱們將獲得如下文件:數據庫
$ ls -l certs total 48 -rw-r--r-- 1 root root 948 Aug 6 05:58 client-cert.csr -rw-r--r-- 1 root root 3973 Aug 6 05:59 client-cert.pem -rw-r--r-- 1 root root 1679 Aug 6 05:58 client.key -rw-r--r-- 1 root root 1675 Aug 6 05:53 root-ca.key -rw-r--r-- 1 root root 1212 Aug 6 05:53 root-cacert.pem -rw-r--r-- 1 root root 1679 Aug 6 05:54 second-ca.key -rw-r--r-- 1 root root 952 Aug 6 05:54 second-cacert.csr -rw-r--r-- 1 root root 4194 Aug 6 05:54 second-cacert.pem -rw-r--r-- 1 root root 948 Aug 6 05:59 server-cert.csr -rw-r--r-- 1 root root 3973 Aug 6 05:59 server-cert.pem -rw-r--r-- 1 root root 1675 Aug 6 05:59 server.key
$ openssl verify -CAfile certs/root-cacert.pem -untrusted certs/second-cacert.pem certs/server-cert.pem certs/server-cert.pem: OK $ openssl verify -CAfile certs/root-cacert.pem -untrusted certs/second-cacert.pem certs/client-cert.pem certs/client-cert.pem: OK $ cat certs/root-cacert.pem > certs/cacert.pem;cat certs/second-cacert.pem >> certs/cacert.pem $ openssl verify -CAfile certs/cacert.pem certs/client-cert.pem certs/client-cert.pem: OK
$ openssl s_server -accept 2009 -key certs/server.key -cert certs/server-cert.pem $ openssl s_client -connect localhost:2009 -CAfile certs/cacert.pem -showcerts Verify return code: 0 (ok)
Note: Client 既可使用 root-cacert.pem
與 second-cacert.pem
合併後的 cacert.pem
,也可使用 second-cacert.pem
。vim
假設你已經成功安裝了 EMQ X,咱們將以前生成的證書一併拷貝到 emqx/etc/certs
目錄下:centos
$ cp certs/* emqx/etc/certs/
而後修改 emqx.conf
配置以下:服務器
listener.ssl.external.keyfile = etc/certs/server.key listener.ssl.external.certfile = etc/certs/server-cert.pem
啓動 EMQ X 並將日誌等級改成 Debug。併發
$ ./emqx/bin/emqx start $ ./emqx/bin/emqx_ctl log set-level debug
使用 openssl s_client 鏈接 EMQ X 併發送一個 Client ID 爲 "a" 的 MQTT Connect 報文。測試
$ echo -en "\x10\x0d\x00\x04MQTT\x04\x00\x00\x00\x00\x01a" | openssl s_client -connect localhost:8883 -CAfile certs/cacert.pem -showcerts Verify return code: 0 (ok)
若是你在 emqx/log/erlang.log.1
中看到如下日誌,說明 SSL 認證成功。
2019-08-06 15:13:30.748 [debug] 127.0.0.1:60737 [Protocol] RECV CONNECT(Q0, R0, D0, ClientId=a, ProtoName=MQTT, ProtoVsn=4, CleanStart=false, KeepAlive=0, Username=undefined, Password=undefined)
EMQ X 繼續保持運行,編譯並啓動 emqtt。
$ git clone -b v1.0.1 https://github.com/emqx/emqtt.git $ cd emqtt $ make $ erl -pa _build/default/lib/*/ebin ## connect to broker 1> {ok, ConnPid} = emqtt:start_link([{client_id, <<"my_client">>}, {ssl, true}, {ssl_opts, [{cacertfile,"../certs/cacert.pem"}]}, {port, 8883}]). {ok,<0.80.0>} 2> {ok, _Props} = emqtt:connect(ConnPid). {ok,undefined} ## subscribe 3> {ok, _Props, _ReasonCodes} = emqtt:subscribe(ConnPid, {<<"hello">>, 0}). {ok,undefined,[0]} ## publish 4> ok = emqtt:publish(ConnPid, <<"hello">>, <<"Hello World!">>, 0). ok ## receive message 5> receive {publish, Message} -> io:format("Message: ~p~n", [Message]) after 1000 -> io:format("Error: receive timeout!~n") end. Message: #{client_pid => <0.80.0>,dup => false,packet_id => undefined, payload => <<"Hello World!">>,properties => undefined,qos => 0, retain => false,topic => <<"hello">>} ok ## disconnect from broker 6> ok = emqtt:disconnect(ConnPid).
鏈接創建成功,能夠正常訂閱發佈,SSL 單向認證測試經過。
EMQ X 繼續保持運行,啓動 mqtt.fx,參照下圖完成配置:
Note: 這裏只能使用 second-cacert.pem
做爲 CA Certificate。
點擊 Connect
按鈕,鏈接成功,而且能夠正常訂閱發佈,SSL 雙向認證經過。
$ openssl s_server -accept 2009 -key certs/server.key -cert certs/server-cert.pem -CAfile certs/cacert.pem -Verify 1 ## Use root CA $ openssl s_client -connect localhost:2009 -key certs/client.key -cert certs/client-cert.pem -CAfile certs/cacert.pem -showcerts Verify return code: 0 (ok) ## Use second CA $ openssl s_client -connect localhost:2009 -key certs/client.key -cert certs/client-cert.pem -CAfile certs/second-cacert.pem -showcerts Verify return code: 19 (self signed certificate in certificate chain)
修改 emqx.conf
配置以下:
listener.ssl.external.keyfile = etc/certs/server.key listener.ssl.external.certfile = etc/certs/server-cert.pem listener.ssl.external.cacertfile = etc/certs/cacert.pem ## 開啓雙向認證 listener.ssl.external.verify = verify_peer ## 禁止單向認證 listener.ssl.external.fail_if_no_peer_cert = true
啓動 EMQ X 並將日誌等級改成 Debug。
$ ./emqx/bin/emqx start $ ./emqx/bin/emqx_ctl log set-level debug
使用 openssl s_client 鏈接 EMQ X 併發送一個 Client ID 爲 "a" 的 MQTT Connect 報文。
## Use root CA $ echo -en "\x10\x0d\x00\x04MQTT\x04\x00\x00\x00\x00\x01a" | openssl s_client -connect localhost:8883 -CAfile certs/cacert.pem -cert certs/client-cert.pem -key certs/client.key -showcerts Verify return code: 0 (ok) ## Use second CA $ echo -en "\x10\x0d\x00\x04MQTT\x04\x00\x00\x00\x00\x01a" | openssl s_client -connect localhost:8883 -CAfile certs/second-cacert.pem -cert certs/client-cert.pem -key certs/client.key -showcerts Verify return code: 19 (self signed certificate in certificate chain)
若是你在 emqx/log/erlang.log.1
中看到如下日誌,說明 SSL 雙向認證成功。
2019-08-06 15:47:03.925 [debug] 127.0.0.1:61343 [Protocol] RECV CONNECT(Q0, R0, D0, ClientId=a, ProtoName=MQTT, ProtoVsn=4, CleanStart=false, KeepAlive=0, Username=undefined, Password=undefined)
EMQ X 繼續保持運行,啓動 emqtt。
$ erl -pa _build/default/lib/*/ebin ## connect to broker 1> {ok, ConnPid} = emqtt:start_link([{client_id, <<"my_client">>}, {ssl, true}, {ssl_opts, [{certfile,"../certs/client-cert.pem"},{keyfile,"../certs/client.key"}, {cacertfile,"../certs/cacert.pem"}]}, {port, 8883}]). {ok,<0.182.0>} 2> {ok, _Props} = emqtt:connect(ConnPid). {ok,undefined} ## subscribe 3> {ok, _Props, _ReasonCodes} = emqtt:subscribe(ConnPid, {<<"hello">>, 0}). {ok,undefined,[0]} ## publish 4> ok = emqtt:publish(ConnPid, <<"hello">>, <<"Hello World!">>, 0). ok ## receive message 5> receive {publish, Message} -> io:format("Message: ~p~n", [Message]) after 1000 -> io:format("Error: receive timeout!~n") end. Message: #{client_pid => <0.182.0>,dup => false,packet_id => undefined, payload => <<"Hello World!">>,properties => undefined,qos => 0, retain => false,topic => <<"hello">>} ok ## disconnect from broker 6> ok = emqtt:disconnect(ConnPid).
鏈接創建成功,能夠正常訂閱發佈,SSL 雙向認證測試經過。
EMQ X 繼續保持運行,啓動 mqtt.fx,參照下圖完成配置:
Note: CA File
可使用 root-cacert.pem
與 second-cacert.pem
合併後的 cacert.pem
,也可使用 second-cacert.pem
。
點擊 Connect
按鈕,鏈接成功,而且能夠正常訂閱發佈,SSL 雙向認證經過。
更多信息請訪問咱們的官網 emqx.io,或關注咱們的開源項目 github.com/emqx/emqx ,詳細文檔請訪問 官方文檔。