EC2 の Amazon Linux 2 インスタンスに Mosquitto をインストールして SSL/TLS 認証を設定する
オープンソースの MQTT ブローカーである Mosquitto を EC2 の Amazon Linux 2 にインストールしてみます。
AWS にも IoT Core サービスにメッセージブローカーという MQTT ブローカーが存在しており、 TLS にも対応しているため、用途に合うようなら、こちらを利用するほうがいいかもしれません。
前提条件・環境
- Amazon Linux 2
- Mosquitto 1.6.1
Mosquitto のインストール
インストールとはいっても、 epel を有効にして、 yum でインストールするだけなので非常にお手軽です。
インストール
$ sudo amazon-linux-extras install epel
$ sudo yum install mosquitto -y
開始・自動起動設定
systemctl start でサービスを開始し、 enable で自動起動設定をしておきます。
$ sudo systemctl start mosquitto
$ sudo systemctl enable mosquitto
特になにも設定しなければ標準の TCP 1883 ポートで起動します。
状態確認
systemctl status で状態を確認して、 Active: active (running)
になっていれば OK です。
$ sudo systemctl status mosquitto
● mosquitto.service - Mosquitto MQTT v3.1/v3.1.1 Broker
Loaded: loaded (/usr/lib/systemd/system/mosquitto.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2020-08-26 16:38:06 JST; 1 day 18h ago
Docs: man:mosquitto.conf(5)
man:mosquitto(8)
Main PID: 18791 (mosquitto)
CGroup: /system.slice/mosquitto.service
└─18791 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
Hint: Some lines were ellipsized, use -l to show in full.
動作確認
なにはともあれ動作確認をしてみます。購読側 (subscriber) と 発行側 (publisher) で2つのターミナルを立ち上げます。
サブスクライブは mosquitto_sub
、パブリッシュは mosquitto_pub
を使用します。
$ mosquitto_sub -t hoge -h localhost
を実行して購読状態にさせておき、別のターミナルで
$ mosquitto_pub -t hoge -h localhost -m "Hello, world"
のように送信して、購読側に下記のように表示されれば OK です。
Hello, world
SSL/TLS による暗号化
デフォルトでは 1883 ポートで暗号化されない設定となっていますので、 SSL/TLS での暗号化を有効にし、 8883 ポートで運用することにします。
ここでは暗号化に自己署名の証明書を利用します。この流れでよくある「オレオレ証明書」です。すでに証明書がある場合は、この項は飛ばしてください。
CA 認証局 証明書作成
秘密鍵作成
まず CA の証明書を作成します。
$ sudo openssl genrsa -des3 -out ca.key 2048
Generating RSA private key, 2048 bit long modulus
................................................+++
.............................+++
e is 65537 (0x10001)
Enter pass phrase for ca.key:パスフレーズ
Verifying - Enter pass phrase for ca.key:パスフレーズ
秘密鍵 ca.key
が生成されます。
証明書作成
秘密鍵から証明書ファイルを生成します。 Common name は myca
など適当な名前にしておきます。その他は特に入力不要です。
$ sudo openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
Enter pass phrase for ca.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:myca
Email Address []:
証明書 ca.crt
が生成されれば OK です。
サーバー証明書作成
秘密鍵の作成
サーバーの秘密鍵を生成します。
$ sudo openssl genrsa -out server.key 2048
Generating RSA private key, 2048 bit long modulus
........................................................................................+++
........+++
e is 65537 (0x10001)
秘密鍵 server.key
が生成されます。
CSR ファイルの作成
サーバーの秘密鍵を用いて CSR (Certificate Signing Request; 証明書署名リクエスト) ファイルを生成します。
こちらの Common name には Mosquitto サーバーのホスト名を指定します。それ以外は CA と同じくなんでもかまいません。
$ sudo openssl req -new -out server.csr -key server.key
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:都道府県
Locality Name (eg, city) [Default City]:市町村
Organization Name (eg, company) [Default Company Ltd]:組織名
Organizational Unit Name (eg, section) []:部署名
Common Name (eg, your name or your server's hostname) []:ホスト名 (サーバーの FQDN など)
Email Address []:メールアドレス
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:<なし>
An optional company name []:<なし>
CSR server.csr
が生成されます。
サーバー証明書を CA で認証
オレオレ認証局で認証したサーバー証明書を生成します。
$ sudo openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650
Signature ok
subject=/C=JP/ST=都道府県/L=市町村/O=組織名/OU=部署/CN=ホスト名/emailAddress=メールアドレス
Getting CA Private Key
Enter pass phrase for ca.key:CA秘密鍵のパスフレーズ
証明書ファイル server.crt
が生成されれば OK です。これがサーバー証明書になります。
証明書の配置
インストールディレクトリ (デフォルトでは etc/mosquitto
) に移動し、 certs
ディレクトリを作成し、 server.crt
, server.key
, ca.crt
を配置します。
※コンフィグでパスを指定しますので、ディレクトリに決まりはありません。
鍵ファイルは読み取り専用にしておきます。
$ cd /etc/mosquitto/
$ sudo mkdir certs
$ sudo cp ~/server.{crt,key} ca.crt ./certs/
$ sudo chmod 400 ./certs/server.key
$ sudo chmod 444 ./certs/ca.crt
$ sudo chmod 444 ./certs/server.crt
$ ls -l /etc/mosquitto/certs/
total 12
-r--r--r-- 1 root root 1424 Aug 25 19:10 ca.crt
-r--r--r-- 1 root root 1306 Aug 25 19:09 server.crt
-r-------- 1 root root 1679 Aug 25 19:09 server.key
コンフィグファイルの設定
コンフィグファイル /etc/mosquitto/mosquitto.conf
を修正して SSL/TLS を有効化します。
-#port 1883
+port 8883
# "openssl rehash <path to capath>" each time you add/remove a certificate.
-#cafile
+cafile /etc/mosquitto/certs/ca.crt
# Path to the PEM encoded server certificate.
-#certfile
+certfile /etc/mosquitto/certs/server.crt
# Path to the PEM encoded keyfile.
-#keyfile
+keyfile /etc/mosquitto/certs/server.key
再起動
修正できたら Mosquitto を再起動します。
$ sudo systemctl restart mosquitto
ログを確認して 8883 ポートで起動していればとりあえず OK です。
$ tail -f /var/log/mosquitto/mosquitto.log
mosquitto[17422]: 1598417779: mosquitto version 1.6.10 starting
mosquitto[17422]: 1598417779: Config loaded from /etc/mosquitto/mosquitto.conf.
mosquitto[17422]: 1598417779: Opening ipv4 listen socket on port 8883.
mosquitto[17422]: 1598417779: Opening ipv6 listen socket on port 8883.
systemd[1]: Started Mosquitto MQTT v3.1/v3.1.1 Broker.
ユーザー認証の設定
クライアントをユーザー名とパスワードで認証するようにします。クライアント証明書を用いた認証方法もありますが、今回はユーザー名とパスワードにしました。
ユーザーの作成
mosquitto_passwd
コマンドでユーザーを作成します。はじめて作成するときは -c
オプションをつけましょう。
$ sudo mosquitto_passwd -c /etc/mosquitto/mqttpass ユーザー名
Password:
Reenter password:
$ cat /etc/mosquitto/mqttpass
ユーザー名:$6$7Y/7oiQLgaDcU53W$+pMc7mdqxO8JRlGEWw5qWY9iVXt4KkSYxauKvniEL87KT+2DS9HhivxuY5MEmxo80O22YYCb/E01NjbqpCNVfg==
パスワードファイルが生成されてユーザー名とハッシュ化されたパスワードが記録されていれば OK です。
他にユーザーが必要な場合は、同様にして追加します (-c
オプションは不要です)。
コンフィグファイルの修正
/etc/mosquitto/mosquitto.conf
を下記のように修正します。
-#allow_anonymous true
+allow_anonymous false
-#password_file
+password_file /etc/mosquitto/mqttpass
これで匿名認証が無効になり、パスワードファイルに記載されたユーザー名とパスワードで認証されるようになります。
Mosquitto を再起動しておきます。
$ sudo systemctl restart mosquitto
動作確認
暗号化設定前と同様に mosquitto_sub/mosquitto_pub で動作確認します。
ポートを -p 8883
で指定するほか、 --cafile
で CA の証明書のパス、 -u
でユーザー名、 -P
でパスワードを指定します。
$ mosquitto_sub -t hoge -h ホスト名 -p 8883 --cafile /etc/mosquitto/certs/ca.crt -u ユーザー名 -P パスワード
を実行して購読状態にさせておき、別のターミナルで
$ mosquitto_pub -t hoge -m "Hello, encrypted world" -h ホスト名 -p 8883 --cafile /etc/mosquitto/certs/ca.crt -u ユーザー名 -P パスワード
のように送信して、購読側に下記のように表示されれば OK です。
Hello, encrypted world
ちなみにホスト名はサーバー証明書の Common name で入力した名前でなければ接続できないはずです。無視して接続する際は --insecure
オプションをつけるとよいそうです。