ArmベースのEC2インスタンスのエントロピー不足問題について

普段RunbookはApple SiliconのMacで開発しており、運用環境も極力Armベースで統一することにしました。これまではx86系のインスタンスを使用していたので、若干不安もありましたが、検証してみると特に問題はないことがわかり、コストメリットもかなり受けられることがわかりました。

ただArmベースのインスタンスについてググってみると、こちらの記事のように、エントロピーが少ないせいでパフォーマンスが低下するので注意しようという情報がいくつもヒットします。

EC2 の Arm では、random デバイスに対するエントロピーがとても少ない場合によってはエラー、もしくは大幅なパフォーマンス低下を引き起こします。

今回は Apache Tomcat(java) を利用して乱数生成する部分のパフォーマンスが、大幅に低下しました。

これは乱数生成で /dev/random を利用するのですが、これに対するリソースが少ないために発生します。

こちらの記事は4年くらい前のものなのですが、本当にそうなのか気になったので調べてみることにしました。

そもそもエントロピーとは何か

情報理論でいうエントロピーとは、いわゆる熱力学でいうエントロピーのことではありません。コンピューターでランダムな値(乱数)を生成することがありますよね。UUIDを生成したり、じゃんけんゲームでコンピューターが何を出すかを決めるのに乱数が使われます。コンピューターが生成する乱数というのは、計算によって出力されるものですから、出力される値は何かしらの法則性に従っており、まったくランダムな真乱数と区別するために、擬似乱数と呼ばれます。でもあまりにも推測されやすい乱数だと、暗号などには怖くて使えませんよね。そこで疑似乱数の中でも、暗号で使えるような強力な疑似乱数があって、それらを暗号論的疑似乱数といいます。では暗号論的疑似乱数を生成するための大元のようなもの(シード)はどこから持ってくるかというと、これはファンやHDDなどのハードウェアのノイズや、キーボードやマウス、マイクといったデバイスからの入力のような、ばらつきが期待できる物理的な環境から取得していて、このようにして外部環境から得た、ばらつきのある情報量のことを、エントロピーと呼びます。

ちなみに、Cloudflareでは、本社の壁に100個のラバライト(カラフルな液体状のライト)を設置して定間隔で写真をとり、そのデータを乱数のシードを得るために使っているらしいです。

lava-lamps.jpg

エントロピーの壁

Armインスタンスのエントロピー

AWSでT4gのEC2インスタンスを起動して、エントロピーの値を確認しました。

$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 22.04.4 LTS
Release: 22.04
Codename: jammy
$ uname -r
6.5.0-1022-aws
$ cat /proc/sys/kernel/random/entropy_avail
256

上の記事ではエントロピーが11しかなかったと書かれてありましたが、256(bits)とのことで、大きいわけではありませんが、小さくもありません。というか、今のLinuxカーネルでは常にこの値は256なのです。

したがって、/proc/sys/kernel/random/entropy_avail には常に 256 である必要があります。これは、ChaCha20 キーのビット単位のサイズです。このファイルにさらに大きな値が含まれることを期待したり、値が "低すぎる" 場合にユーザーがアクションを実行することを期待した過去の文書は無視できます。

乱数を生成するとエントロピーが消費される?

では実際に乱数を生成してみます。

$ cat /proc/sys/kernel/random/uuid
2e23898d-28c1-47fe-969a-f4245c5f1a13
$ cat /proc/sys/kernel/random/entropy_avail
256

変化ありません。uuidくらいでは甘いのかもしれないのでもっとやってみます。

$ cat /dev/random | hexdump
...省略
005bff0 6266 2a50 c110 4b73 6752 390d 33e4 6a2f
005c000 15c8 2446 e2c1 8917 700d 9674 dc6d 4153
005c010 ffb9 bb1b e105 f6e1 6445 1c7e 8da7 b530
005c020 59c7 1451 c007 6189 09cf b807 e68e a861
005c030 ac8c 1309 924e d878 7296^C
$ cat /proc/sys/kernel/random/entropy_avail
256

256ビットなんかとっくに消費しているだろうに、やはり変わりませんね。これはどういうことでしょうか?

上のArch Linuxのサイトにはこうも書かれています。

/dev/random は真のランダム データを提供することを目的とするのではなく、むしろ暗号的に安全なランダムデータ (現実世界のすべてのユースケースに十分なデータ) を提供することを目的としているため、それを読み取ってもカーネルのエントロピープールが枯渇することはなくなりました。

/dev/random/と/dev/urandomについて

最初に引用した記事では、/dev/randomの性能が大幅に劣化すると書かれてありました。実はこれはLinuxカーネル5.6より前の話です。かつては、/dev/randomは読み出されるたびにエントロピーを消費して、エントロピーが枯渇すると、新たに環境ノイズが得られるまで読み出しがブロックされていました。なので、ブロックが許容できない場合は、/dev/urandomを使って疑似乱数を取得するのが良いということになっていました。しかし、カーネル5.6からは、どちらもエントロピーを消費しない疑似乱数になっています。違いは、/dev/randomのほうはブート時にエントロピープールからシードを取得できるまでブロックすることだけです(これもx86-64ではCPUベースでエントロピーが生成できるので違いはまったくないらしいです)。

つまり、Armベースのインスタンスでエントロピー不足が問題になるのは、カーネル5.6より前のLinuxを使っている場合ということですね。5.6がリリースされたのが2020年3月ですから、それ以降のOSだとほぼ問題にならないはずです。

まとめ

  • entropy_availの値は常に256
  • /dev/random/と/dev/urandomの違いはほぼない
  • ArmベースのインスタンスにはLinuxカーネル5.6以降を使おう

なにか間違いやご指摘などありましたら@ryokdyまでお願いします。