先日気付いた(というか、それまでは「まあいいか」にしていた)、バックアッププログラム(duplicacy)が使うクラウドストレージの認証情報とバックアップデータの暗号鍵(以下、認証情報)が平文で保存されていた件。デスクトップはGNOME keyring(以下、GKR)を使って何とかできたが、サーバが難しかった。
というのは、デスクトップと違ってサーバには通常はログインしないので、GKRをアンロックする(= パスワードを入れる)契機がないのだ。前回書いたように、そのパスワードをサーバに安全に保存するのは難しい(それを暗号化して保存するとしても、そのための鍵・パスワードをどうするかになって、キリがない)。
「普通」は どうしているのか気になるが、少し調べても分からなかったので(前回書いたように、今だとTPMを使っているのかも知れない)、自分なりに何とかした。
以下のような骨組みにした。
- デスクトップと同様に、クラウドストレージの認証情報はGKRのキーリングに保存する。
- キーリングのパスワードはサーバには置かずに管理(記憶または保管)する。
- サーバが(再)起動後はキーリングがアンロックされていないので、認証情報を使うプログラム(具体的にはクラウドストレージへの自動バックアッププログラム)は失敗する。
- 失敗するとメールで通知が来る。
- サーバは基本的には再起動しないが、自動更新の内容によっては再起動する。
- 再起動はそれほど頻繁ではない(ただし、連続する場合もある)。
- 失敗の通知が来たら、僕が手で(デスクトップから)サーバのキーリングをアンロックする。
- アンロックするまで自動バックアップされないが、バックアップは数時間ごとなので、僕が「ちゃんと」して居れば大きな問題にはならない。
- 今はありそうもないが、長期の外出中にサーバが再起動した場合などにはアンロックできずに問題になりそうなので、その時に考えたい。
- アンロックするには、パスワードがあってサーバにsshできればいいので、いざとなればスマフォでもできそうだ。
- → 試したらできた。: Androidのターミナルアプリ(Termiusを使った)でsshでサーバにログインし、アンロックプログラム(gkr-unlock.sh)を起動し、パスワードマネージャ(KeePass)からキーリングのパスワードをコピー・ペーストして入力すれば良い。最後の方に載せた画面例と同様の操作である。 (5/24 9:09)
- アンロックするには、パスワードがあってサーバにsshできればいいので、いざとなればスマフォでもできそうだ。
上の仕組みを実装し、どうにか動くようになった。細かい点を以下に書く。
- デスクトップと異なり、認証情報はGKRのログインキーリング(以下、キーリング)に格納される(デスクトップはデフォルトキーリング)。
- GKRの仕組みを完全に理解していないせいもあるが、seahorse(GUIアプリ)を使わずにアンロックできるのは、(現在は)ログインキーリングだけなので、仕方ない。
- gnome-keyring-daemon --unlockでログインキーリングをアンロックする。
- 昔は任意のキーリングをアンロックするプログラム(pam-keyring)があったようだが、なぜか なくなってしまった。
- GNOMEのLibsecretのAPIを使えばできそうで、試したらデフォルトキーリングのアンロックはできたが、「ちょっと試す」以上だと複雑かつ情報が少なくて、任意のキーリングについては途中で挫けた。。。
- 認証情報はデスクトップのデフォルトキーリングの用法にならい、属性serviceとusernameで種類を識別・区別できるようにした(要するに、同様に格納した)。
- サーバではseahorseが動かないので、キーリングの初期化と認証情報の格納にはgnome-keyring-daemonとsecret-toolを用いた。
- 以下の手順でキーリングを初期化・格納した。
- 既存のキーリング(~/.local/share/keyrings)を使わないなら削除する。
- dbus-launch --sh-syntax のようにしてdbus-daemonを起動する。
- 上で表示された文字列(DBUS_SESSION_BUS_ADDRESS=...を実行して、Dbusの環境変数を設定する。
- echo -n パスワード | gnome-keyring-daemon --unlock のようにして空のキーリングを作り、パスワードを設定する。 (既存のキーリングの場合は単にアンロックする。)
- secret-tool store --label="ストレージ名" service アプリ名 username ストレージID のようにして、それぞれの認証情報を格納していく。
- 作成したキーリングをアンロックするプログラム(gkr-unlock.sh)は、アンロック後、GKRにアクセスするのに必要なDbusの環境変数(DBUS_SESSION_BUS_ADDRESS)をファイルに保存する。
- この辺りの基本処理のコードを以下に示す(実際とは若干異なる)。dbus-launchがGKRにアクセス可能なDBUS_SESSION_BUS_ADDRESSを設定するので、それをファイルに保存する。
- GKRの仕組みを完全に理解していないせいもあるが、seahorse(GUIアプリ)を使わずにアンロックできるのは、(現在は)ログインキーリングだけなので、仕方ない。
echo "$PW" | dbus-launch
sh -c "gnome-keyring-daemon --unlock -d -r;
echo export DBUS_SESSION_BUS_ADDRESS=
\$DBUS_SESSION_BUS_ADDRESS
> $dbus_env_save_file" >&- 2>&- &
※見やすくするため改行したが、実際には全部が1行である。
※$PWには あらかじめ入力したキーリングのパスワードを、$dbus_env_save_fileには環境変数を保存するファイルのパスを格納しておく。
- 認証情報を使うプログラムは、secret-toolでキーリングにアクセスする。
- デスクトップと異なり、Dbusの環境変数が設定されていないため、そのままではアクセスできないので、環境セットアッププログラム(setup_dupl_agk.sh)経由で起動する。
- デスクトップのcrontabでの実行時も同様なので、デスクトップと同じ環境セットアッププログラムが使えるようにした。
- 環境セットアッププログラムは、上記のキーリングをアンロックするプログラムが保存したDbusの環境変数を読み込み・設定・exportして、目的のプログラムを起動する。
- なお、dbus-daemonやGKRを起動したユーザー以外(具体的にはroot)はGKRにアクセスできないようなので、バックアッププログラムが使うクラウドストレージの認証情報をキーリングから取得し、環境変数に設定・exportしてバックアッププログラム(duplicacy)が使えるようにする。
- duplicacyは、設定ファイルやGKRだけでなく、環境変数からも認証情報を取得できる。
- Dbusの環境変数を保存したファイルは再起動すると なくなるので、再起動後は自動的に失敗する(古い無効な環境変数でアクセスして、おかしくなることはない)。
- なお、dbus-daemonやGKRを起動したユーザー以外(具体的にはroot)はGKRにアクセスできないようなので、バックアッププログラムが使うクラウドストレージの認証情報をキーリングから取得し、環境変数に設定・exportしてバックアッププログラム(duplicacy)が使えるようにする。
- デスクトップと異なり、Dbusの環境変数が設定されていないため、そのままではアクセスできないので、環境セットアッププログラム(setup_dupl_agk.sh)経由で起動する。
- デスクトップからは、サーバのキーリングをアンロックするプログラム(gkr-unlock.sh: サーバと同じもの)を実行する。
- gkr-unlock.shは、sshでサーバに接続して、サーバ側のキーリングをアンロックするプログラム(gkr-unlock.sh)を起動する。
- 基本的には、サーバでgkr-unlock.shを起動するとキーリングのパスワードを要求して来るので、(手で)入力してアンロックする。
- ただ、毎回パスワードを手で入力するのは面倒なので、サーバのキーリングのパスワードをデスクトップのキーリングに入れておき、アンロックするプログラムはそのパスワードを自動で取得してサーバに送るようにした。
- そのため、サーバのキーリングのパスワードを複雑なものにできる。
- また、アンロック時はサーバにssh接続するための鍵のパスフレーズを入れるだけで良い(sshエージェントを使えばそれも不要になるとのことだが、それはしていない)。
- sshエージェントを使えば、サーバの再起動を検知したら自動でアンロックすることも不可能ではないだろう。
- そのため、サーバのキーリングのパスワードはパスワードマネージャ(Keepass)とGKRの2箇所に重複して保管されて管理の点で良くないが、利便性のためなので「仕方ない」とした。
いつものように、今回作ったGKRアンロックプログラムは思ったより複雑になった(その前に作った環境セットアッププログラムはもっと複雑だ)。僕が求め過ぎるためにそうなるのか、もっと単純・簡単な方法があるのか、今は分からない(そして、「ちゃんと使えれば良し」で そのままに・・・w)。
以下に、デスクトップからサーバ(ホスト名をserverとする)のキーリングをアンロックする画面(?)の例を示す(一部を変更した)。起動後、"Enter passphrase-"のところでssh鍵のパスフレーズを入れる。"bin/gkr-unlock.sh"で始まる行はサーバ側のアンロックプログラムの出力である。
$ ./gkr-unlock.sh server
./gkr-unlock.sh: Obtaining remote host server's GKR password: attrs.: service=gkr-unlock, username=server.
./gkr-unlock.sh: Executing on rem_host: server: bin/gkr-unlock.sh
Enter passphrase for key '/home/user/.ssh/server_id':
bin/gkr-unlock.sh: Enter GKR password (empty to abort):
bin/gkr-unlock.sh: Starting dbus-daemon and gnome-keyring-daemon...
bin/gkr-unlock.sh: Saved dbus-env. to /run/user/9999/dbus-env.
bin/gkr-unlock.sh: Succeessfully unlocked GKR.
DbusやGKRのプロセスを停めて基本的な確認はしているものの、まだ、実際にサーバを再起動して そのあとの復旧(再アンロック)手順に問題がないかを試して居ないので、何か問題が起こりそうだ。早目に再起動して確認・対処することもできるが、(面倒だしw、)通常はちゃんと動いているから 自動更新での再起動を待っている(そして慌てる)。
→ (5/26 6:40) 今朝、自動更新での再起動があり、(意外にも?)想定した復旧(再アンロック)手順で うまく行った。
セキュリティを改善できたものの、(詳しくないなりに考えると)気になることは ある。
- 基本的にキーリングは常時アンロックされたままなので、システムに侵入されて、ユーザー権限が取得されてDbus関係の環境変数が分かれば、GKRに接続できて認証情報を取得できてしまう。
- デスクトップでも同じことではあるが、サーバだと更に心許ない。
- ただ、ユーザー権限が取得されても「大丈夫」なように防御するのは大変難しい(SE Linuxを使いこなす必要があるのではないか。それでも完全かは分からない)ので、「仕方ない」とした。
- それでも、この対処をすることで、認証情報が平文で設定ファイルに保存されなくなったので、仮に設定ファイルが流出しても被害が少ないという点で意味がある。
- まあ、本当に「最低限の対処」をしたということだ。
- その点で、GKRを信頼し切って、全部の認証情報を格納する「パスワードマネージャ」として使うのは良くないと思う(実際に、僕はそうしていない)。
- gpgにはアンロックのタイムアウトがあるので良いが、サーバでは使いにくい。
- 書いたあとで思い付いたが、認証情報を使うたびにアンロックするようにするのがベターかも知れない。
- その時のパスワードは、動き続けるプロセスが保持するようにする。そのプロセスのメモリを読まれない限り、安全だろう。
- とは言え、ユーザの権限を取得されれば駄目だし、そのプロセスはアンロックされたままのGKRと同じことか。
サーバではないけど、スマフォの権限の制限がAndroidですら厳しい(僕にしてみれば「Linuxなのに何もできない」)のは、こういうことに起因するのだろうと思った。
それから、クラウドストレージに関しては改善できたが、気になるものは他にも いろいろある(例: ブログサーバプログラムの設定ファイル)。簡単にはできないので、いい案を思い付いて やる気があれば着手したい。あるいは、既存の何かがあるかも知れない。
まあ、まず、一般的なサーバでは「普通」はどうしているかを調べるべきかも知れない。
SE Linuxだけど、手に負えなくて「全部解除」とかいうオチだったりはしないよね?w