TOC
苦労の連続しかなかった…
Rocket.ChatというSlackと似た使い勝手のFLOSSがあります。これをUbuntu 20.10を動かしているRPi4でローカルネットワーク用にオンプレミスで運用しようと決めてから、実際に運用できるようになるまで膨大な時間が掛かったので、その手順のまとめ記事です。
引っかかったポイント
様々なソフトウェアのインストールやデプロイは、Dockerによるコンテナ運用が一般的になるにつれてどんどん簡単になっています。今回紹介するRocket.Chatもその例に漏れず、snapを使用した配布をしているので、とても簡単に構築できる…はずでした。
- Android版Rocket.ChatアプリはTLS接続が必須
- Androidは名前解決に(アプリ側で特別な実装をしない限り)mDNSを使うことができない
- 外部に公開しない運用をした場合、Let’s EncryptやZeroSSLにTLS証明書を発行してもらうことができない
- Ubuntu 20.04以降ではsnapによる構築ができなかった(18.04はできるらしい)
なので、上記にひっかからないRocket.Chatの運用はとても手間いらずで簡単にできます。
ARM版のsnapパッケージも準備されているので、RPi4でも簡単に構築できる…はずでした。
手順のサマリー
結局、作業が膨大になったので、やるべきことを先にまとめます。
- Rocket.Chatのsnapパッケージをインストール
- Androidアプリをインストール
- snap set rocketchat-server caddy-url=https://rpi4.internal
- snap set rocketchat-server caddy=enable
- (TLS接続するので)DNSで名前を引けるようにする
- TLS証明書を準備
- サーバー側に秘密鍵とCRTを配備
- /var/snap/rocketchat-server/current/Caddyfileを作る
- クライアント側にCAの公開鍵をインポート
- クライアントからアクセス
Rocket.Chatのインストール
Install Rocket.Chat Server on Linux | Snap Store
snapでインストールします。今回はRpi4で実行しているのでARM版がインストールされます。
# snap install rocketchat-server
rocketchat-server (3.x/stable) 3.7.4 from Rocket.Chat (rocketchat✓) installed
Rocket.Chatの起動
インストールが終わったら、そのまま起動します。
# snap start rocketchat-server
Started.
デフォルトではTCPの3000番ポートを使うので、ブラウザから http://192.168.1.210:3000/ などして接続すれば初期設定から使い始められます。
ここからが長いです。
Androidアプリの接続先は暗号化されていないといけない
Rocket.Chat - Google Play のアプリ
This is the correct behavior. We’re following Google recommendation of disallowing insecure connections.
去年の10月リリースの4.11.0からGoogleのススメに従っているようです。
Release Version: 4.11.0 · RocketChat/Rocket.Chat.ReactNative · GitHub
ブラウザから接続すれば問題ないので、妥協できるポイントではあるんですが、使い勝手はネイティブアプリの方が良いだろうと見立ててなんとか回避していくことに。
オフィシャルの手順をなぞるとどのくらい引っかかるのか試してみる
最短手はsnap用の変数を3つ設定してsnap run rocketchat-server.initcaddy
を実行することになっています。
# snap set rocketchat-server caddy-url=https://rpi4.internal
# snap set rocketchat-server caddy=enable
# snap set rocketchat-server https=enable
error: cannot perform the following tasks:
- Run configure hook of "rocketchat-server" snap (run hook "configure":
-----
dig: error while loading shared libraries: libdns.so.162: cannot open shared object file: No such file or directory
Error: Can't resove DNS query for rpi4.internal, check your DNS configuration, disabling https ...
-----)
Caddy Error When Enabling HTTPS · Issue #41 · RocketChat/Rocket.Chat.RaspberryPi · GitHub
digでDNSを拾っている時に共有ライブラリがないという結構根本的におかしいエラーが出ていて、結果Ubuntu20.04以降では現時点で64bit ARMのMongoDBではサポートされないので、64bit ARMでsnapで使う場合は18.04を使ってくれという本末転倒感満載のレスがあったため、今回この3手でのTLS対応を諦めました。
以降、Caddyを使わずに普通にTLS証明書を使って暗号化、ついでにTLSではIPアドレスを使うことはまずなく、ドメインで名前解決する必要があるだろうという判断でDNSサーバーも構築しました。
DNSサーバーをLAN内に構築
ローカルネットワーク用にdnsmasqでDNSサーバーを構築する
RPi4にDnsmasqをインストールして名前で引けるようにしました。この結果、LAN内からRPi4は rpi4.internal というドメインで引けるようになっています。
TLS証明書を準備する
easy-rsaでプライベートCA(認証局)を作ってオレオレ証明書ではないローカル用TLS証明書の管理をする
CAは構築したので、
- 秘密鍵の生成
- 秘密鍵からのCSRの生成
- CAでのCSRへの署名
- 署名結果のCRTの配置
をします。
秘密鍵の生成・CSRの生成
opensslコマンドで秘密鍵とCSRを生成します。指定している引数、入力内容は以下の通りです。
- key 秘密鍵のファイル名(
rocketchat.privkey
) - addext SANに指定するドメイン(
subjectAltName = DNS:rpi4.internal
) - Common Name ドメイン(
rpi4.internal
) out CSRのファイル名(
rocketchat.csr
)$ openssl req -new -key rocketchat.privkey -out rocketchat.csr -addext 'subjectAltName = DNS:rpi4.internal' 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) [AU]: State or Province Name (full name) [Some-State]: Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]: Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:rpi4.internal Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
補足:Chrome58以降ではドメインとCN(Common Name)の一致ではなくドメインとSAN(Subject Alternative Name)が一致する必要がある
以前はCNがドメインと一致していることをChromeが判定していましたが、Chrome58以降ではドメインとSANが一致している必要があります。
Rocket.ChatのAndroidアプリでもCNの一致のみでは信頼されない挙動に見えます。
CAでのCSRへの署名
生成したCSRをCAで署名してCRTを生成します。
まず、CSRをrocketchatという名前でインポートします。/tmp/rocketchat.csr
がインポートするCSRのファイル名です。
$ cd ~/easy-rsa
$ ./easyrsa import-req /tmp/rocketchat.csr rocketchat
Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.1.1f 31 Mar 2020
The request has been successfully imported with a short name of: rocketchat
You may now use this name to perform signing operations on this request.
内容を確認してrocketchatに署名します。
$ ./easyrsa sign-req server rocketchat
Note: using Easy-RSA configuration from: ./vars
Using SSL: openssl OpenSSL 1.1.1f 31 Mar 2020
You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.
Request subject, to be signed as a server certificate for 1080 days:
subject=
countryName = AU
stateOrProvinceName = Some-State
organizationName = Internet Widgits Pty Ltd
commonName = rpi4.internal
Type the word 'yes' to continue, or any other input to abort.
Confirm request details: yes
Using configuration from /home/ca/easy-rsa/pki/safessl-easyrsa.cnf
Enter pass phrase for /home/ca/easy-rsa/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
countryName :PRINTABLE:'AU'
stateOrProvinceName :ASN.1 12:'Some-State'
organizationName :ASN.1 12:'Internet Widgits Pty Ltd'
commonName :ASN.1 12:'rpi4.internal'
Certificate is to be certified until Jan 5 16:08:27 2024 GMT (1080 days)
Write out database with 1 new entries
Data Base Updated
Certificate created at: /home/ca/easy-rsa/pki/issued/rocketchat.crt
最終行にあるように、/home/ca/easy-rsa/pki/issued/rocketchat.crt
がCRTなので、これをRocket.Chat側にコピーします。
秘密鍵とCRTの配備
秘密鍵のファイルであるrocketchat.privkey
とCAが署名したrocketchat.crt
をRocket.Chatが見える場所にコピーします。ここでは/var/snap/rocketchat-server/common/key/
を作って、その下に2ファイルをコピーします。
# mkdir /var/snap/rocketchat-server/common/key/
/var/snap/rocketchat-server/current/Caddyfile の作成
このファイル自体snapパッケージをインストールした時点では存在しませんが、作成するとネットワークの設定を読み込むようになります。
https://rpi4.internal
tls /var/snap/rocketchat-server/common/key/rocketchat.crt /var/snap/rocketchat-server/common/key/rocketchat.privkey
proxy / localhost:3000 {
websocket
transparent
}
2行目のtlsの後にCRTファイル、秘密鍵ファイルを記載したら、caddyを再起動します。
# systemctl restart snap.rocketchat-server.rocketchat-caddy
これでRocket.Chat側の準備は完了です。なお、上記の内容だと443番ポートを掴んでしまうため、後々の運用に響かないよう、HTTPSで接続するポートを8443に変更しています。
https://rpi4.internal:8443
tls /var/snap/rocketchat-server/common/key/rocketchat.crt /var/snap/rocketchat-server/common/key/rocketchat.privkey
proxy / localhost:3000 {
websocket
transparent
}
クライアント側の準備
公開鍵のインポート
プライベートCAの構築エントリに記載している手順を再掲します。対象はUbuntu 20.04とAndroidです。
easy-rsaでプライベートCA(認証局)を作ってオレオレ証明書ではないローカル用TLS証明書の管理をする
システムにインポート
update-ca-certificates
ca.crtをscpでコピーするか中身のbase64の文字列をコピーして~/ca.crt
というファイルに保存したら、/usr/local/share/ca-certificates/
ディレクトリにコピーします。
$ sudo mv ca.crt /usr/local/share/ca-certificates/
端末上にCAの公開鍵を反映します。
# update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
Adding debian:ca.pem
done.
Updating Mono key store
Mono Certificate Store Sync - version 6.12.0.107
Populate Mono certificate store from a concatenated list of certificates.
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.
Importing into legacy system store:
I already trust 138, your new list has 139
Import process completed.
Importing into BTLS system store:
I already trust 138, your new list has 139
Import process completed.
Done
done.
これで、この端末はCAが署名した証明書を信頼します。
$ ls -l /etc/ssl/certs/ | grep ca.pem
lrwxrwxrwx 1 root root 6 1月 20 22:57 2352eff5.0 -> ca.pem
lrwxrwxrwx 1 root root 39 1月 20 22:57 ca.pem -> /usr/local/share/ca-certificates/ca.crt
但し、ブラウザのキャッシュが関係しているのかわかりませんがこの状態で接続できるようになったのは手元ではEpiphanyのみでした。(後で確認したRocket.Chatの画面)
certutilコマンドで$HOME/.pki/nssdbにインポート
update-ca-certificates
コマンドで反映した後もChromium Edge(Dev)では状態が変わらなかった(かつ、後述するChromiumと違って証明書のインポート設定を開けなかった)ので、他にシステム的に反映させられそうなものを検索した結果、$HOME/.pki/nssdb
がユーザー単位で操作できそうだったので、ここにインポートします。
libnss3-tools
をインストールします。
# apt-get install libnss3-tools
現在格納されている証明書を見てみます。
$ certutil -d sql:$HOME/.pki/nssdb -L
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
1つもありませんでした。/home/hogehoge/privateca.crt
というファイル名の証明書をインポートします。-t
で指定する属性はよくわかりませんでしたがとりあえずこれで動くことを確認しています。
$ certutil -d sql:$HOME/.pki/nssdb -A -t "CT,c," -n privateca -i /home/hogehoge/privateca.crt
$ certutil -d sql:$HOME/.pki/nssdb -L
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
privateca CT,c,
この後、Chromium Edge(Dev)を起動し直すかリロードすることで信頼されるようになることを確認できました。
Chromiumにインポート
Chromiumの場合どこからルート証明書を読み取っているのかわからなかったので設定画面から直接インストールします。Chromiumの場合の手順です。
システムではインポートしたはずですが、相変わらず信頼されていない状態が続いています。
Chromiumの設定から「セキュリティ」を選択します。
「詳細設定」にある「証明書の管理」を選びます。
「認証局」タブの「インポート」ボタンを押します。
CAの公開鍵ファイルを開きます。
信頼する種別を選択する画面になるので、今回はとりあえずウェブサイトの識別で信頼します。
インポートが終わると、internalというCAがインポートされたことがわかります。
信頼されていなかったタブに戻ってリロードすると、証明書が有効扱いに変更されたことがわかります。
Androidにインポート
CA公開鍵(privateca.crt
)をAndroidにコピーして、「設定」→「セキュリティ」→「暗号化と認証情報」→「ストレージからのインストール」を選択します。
CAの公開鍵ファイルを選択して認証をすると、証明書の名前を指定する画面になります。
名前を指定してOKをタップすると、信頼できる認証情報に追加されます。
これで設定が問題ない場合、Android上のRocket.Chatアプリからもログインできるようになります。
Mattermostは?
SlackのクローンといえばRocket.Chat以外にも代表的なものの一つにMattermostがあります。Goで実装されていることもあり消費リソースの観点からも魅力的な選択肢でした。Dockerイメージでも配布されているのでインストールもある程度簡単にできそうに見えたというのもあります。
しかし、ARM版については執筆時点でもオフィシャルでは消極的で、セットアップ手順も纏まっているものがなさそうでDockerで運用するのも難しそうだったので、今回はスルーしました。
Raspberry Pi 4, Mattermost inside Docker? - Troubleshooting - Mattermost Discussion Forums
ELF: not found · Issue #387 · mattermost/mattermost-docker · GitHub
スポンサーリンク
comments powered by Disqus