2016年12月31日

Linux 4.9 が封印解除されたけれども・・・

今年はずっと「 Linux カーネルのメモリ管理機構の闇」と戦い続けてきました。

Linux カーネルのメモリ管理機構は「他の誰かが自分のために進捗を出してくれているから自分は余計なことを考えなくていい」という楽観論で動いています。そして、全員が同じ考えで動いたとき、誰も進捗を出せなくなり、システムは静かにハングアップしてしまいます。なんだか、責任の所在が不明な、某市場移転問題みたいですねぇ。

Linux 4.6 で OOM reaper が導入され、 Linux 4.9 では「 OOM killer が発動できる限りは OOM livelock 状態に陥らないことを証明できる」ようになる・・・ことを目指していました。しかし、誰も進捗を出せなくなったことを知らせてくれる仕組みとして、ストールしている間は10秒毎に警告を出力するという楽観的な修正が取り込まれたことにより、 Linux 4.8 までは存在しなかった「ロックを獲得したままバッファが空になるまで永遠に待ち続けるA v.s. ロックを獲得できないことでバッファへの追加を永遠に続けるA以外の全員」という新しい OOM livelock 状態が発生してしまいました。よって、残念ながら「 OOM livelock 状態に陥らないこと」を証明できませんでした。

この問題は printk() がバッファが空になるまで永遠に待ち続けることが原因ということにされたため、バッファを空にする処理を専用のカーネルスレッドにオフロードすることで解消される見込みです。しかし、本当の原因は「ロックを獲得したままスリープしてしまうA v.s. ロックを獲得できないままビジーループをしてしまうA以外の全員」であるため、「ロックを獲得できなかった場合はスリープすることで、ロックを獲得しているAの処理を先へと進める」というのが正しい修正だと思うのですが、そのような修正を加えることにより予期せぬ副作用が発生することを恐れているため、採用される見込みはありません。問題を指摘しても、「 DoS 攻撃を受けていて手遅れだ」という返事。原因がカーネル側にあり、それを修正する方法が存在していても、想定を超える負荷が掛かったら諦めるしかないという、セキュリティとトラブルシューティングをやっている人の視点としては納得いかない世界なのです。こんな調子では、 DirtyCOW のような脆弱性が見つかるのも、当然かもなぁ。

ストールしている間は10秒毎に警告を出力するという楽観的な修正には、全員が同じ考えで動いた場合には機能しないという致命的な欠陥があり、問題が発生しているかもしれないことを知らせるという watchdog としての役割を果たせません。この処理を、専用のカーネルスレッドにオフロードすることで、全員が同じ考えで動いてしまった場合でも機能するようにするという提案を続けています。果たして、採用されるのでしょうか?

posted by 熊猫さくら at 11:34| Comment(0) | TrackBack(0) | Linux

2016年11月05日

セキュリティ・キャンプ2016で使用した、熊猫のテキストを公開しました。

RHEL 7.3 が封印解除されたので、自主規制を解除します。

今年のセキュリティ・キャンプでは、 Linux カーネルのメモリ管理機構の闇について扱いました。3年超の期間を費やし、3000通超の関連メールを送受信し、そして、今年1月からは会社の業務時間の大部分も使わせてもらいながら対応した、膨大な活動履歴の中から抽出したものです。

講義資料では、20年前から存在していたと考えられる脆弱性である CVE-2013-4312 および CVE-2016-2847 の発見から始まり、ずるずると闇に引き込まれていき、幾つかの問題について光を取り戻すまでを描いています。

CVE-2013-4312 および CVE-2016-2847 については RHEL 7.3 のカーネルで修正されていますので、信頼できないユーザがログインする可能性のあるシステムではカーネルをアップデートしてくださいね。先月、 DirtyCOW ( CVE-2016-5195 )への対処でカーネルをアップデートしたばかりだとは思いますが。

メモリ管理機構に起因したシステムのハングアップが発生しても、それを平均的なシステム管理者でも認知できる仕組みが存在しないため、どれくらいの頻度で発生しているのかについての情報はありません。サポートセンタに「今回のハングアップに関して、メモリ管理機構が原因の可能性はあるか?」と照会しても、「判断できない」としか答えられないのです。もし、「無い」とか「低い」とか回答するようなサポートセンタを見つけたら、この資料を見せてやってくださいな。(笑)

posted by 熊猫さくら at 13:30| Comment(0) | TrackBack(0) | Linux

2015年12月21日

環境変数 TZ の謎

とあるプログラムのパフォーマンス測定をしていたら、 localtime() 関数の処理で予想外に時間がかかっていることが判明しました。

そこで、 localtime() 、 localtime_r() 、 gmtime() の3つについて、どれくらいの時間がかかっているのかを簡単に計測してみました。

$ gcc -Wall -O3 -x c - << "EOF"
#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
        int i;
        time_t now = time(NULL);
        struct tm tm0 = { };
        struct tm *tm;
        if (argc == 3)
                for (i = 0; i < 100000000; i++)
                        tm = gmtime(&now);
        else if (argc == 2)
                for (i = 0; i < 100000000; i++)
                        tm = localtime_r(&now, &tm0);
        else
                for (i = 0; i < 100000000; i++)
                        tm = localtime(&now);
        printf("%04u/%02u/%02u %02u:%02u:%02u\n",
               tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
               tm->tm_min, tm->tm_sec);
        return 0;
}
EOF

localtime()localtime_r()gmtime()
$ time ./a.out$ time ./a.out 1$ time ./a.out 1 2

仮想化環境での簡単な計測なのであまり正確ではありませんが、確実に localtime() 関数は gmtime() 関数と比べて遅いようです。

localtime()localtime_r()gmtime()
real 2m6.615s
user 0m51.170s
sys 1m15.550s
real 0m38.278s
user 0m38.309s
sys 0m0.000s
real 0m3.995s
user 0m3.994s
sys 0m0.002s

この原因は、 localtime() 関数は呼び出しの度に、 /etc/localtime の内容が変化していないかどうかを確認するために stat() システムコールを発行しているためです。

CentOS 7 では /etc/localtime が ../usr/share/zoneinfo/Asia/Tokyo へのシンボリックリンクとなっているため、 stat() システムコールでの無駄を減らすために、 /etc/localtime を /usr/share/zoneinfo/Asia/Tokyo のコピーに置き換えて実験してみました。シンボリックリンクを辿らないで済む効果はあるようです。

localtime()localtime_r()gmtime()
real 1m22.785s
user 0m52.446s
sys 0m30.407s
real 0m37.940s
user 0m37.970s
sys 0m0.000s
real 0m3.951s
user 0m3.954s
sys 0m0.000s

次に、環境変数 TZ で zoneinfo ファイルの場所を指定( export TZ=:Asia/Tokyo )して実験してみました。すると、 stat() システムコールが発行されなくなくなった分、 localtime() 関数が速くなりました。

localtime()localtime_r()gmtime()
real 0m39.887s
user 0m39.919s
sys 0m0.002s
real 0m37.597s
user 0m37.629s
sys 0m0.000s
real 0m3.970s
user 0m3.971s
sys 0m0.002s

次に、環境変数 TZ でタイムゾーン情報を指定( export TZ=JST-9 )して実験してみました。すると、 localtime_r() 関数が gmtime() 関数と同じレベルまで速くなりました。

localtime()localtime_r()gmtime()
real 0m8.735s
user 0m8.740s
sys 0m0.003s
real 0m6.733s
user 0m6.738s
sys 0m0.000s
real 0m6.647s
user 0m6.651s
sys 0m0.001s

でも、ちょっと不思議なことが起きてますよね?何故か、 gmtime() 関数は環境変数 TZ にタイムゾーン情報が指定されていると遅くなってしまうようです。

posted by 熊猫さくら at 19:59| Comment(0) | TrackBack(0) | Linux

2015年08月18日

セキュリティ・キャンプ2015で使用した、熊猫のテキストを公開しました。

去年から今年にかけて、例えば Shell Shock 脆弱性のような、OSレイヤーでの重大な脆弱性が見つかり、セキュリティ意識の高い人たちの間では「 SELinux を使おうよ」という機運が高まっているのかもしれません。しかし、サポートセンターでの経験より、「 SELinux を使ってトラブルが起きても対処できない」という人たちも大勢いることが判りました。そこで、改めて「OSの挙動を知って、OSレイヤーのセキュリティについて考えてみよう」と思い、今年度は「 TOMOYO / AKARI / CaitSith ハンズオン」というテーマにしました。

読むためのPDF版コピペするためのテキスト版

今回のテキストは、事前学習資料部分と当日学習資料部分の2部構成になっています。

事前学習資料部分では、「講義で使う環境に慣れてもらう」ことを意図して、「 PXE ブートして sl コマンドが走る Linux 環境を作る」ことに挑戦していただきました。 Scientific Linux ならぬ、 SL Linux です。(これは、昨年度のキャンプの企業見学からの帰りのバスの中での雑談から思いついたネタです。)

当日学習資料部分では、 TOMOYO をメインライン化するまでのドタバタ劇とか、3年間 RHEL システムのトラブルシューティング業務に従事して痛感した組織の問題点とかのような、技術的に Linux システムに詳しくない人にも何かの役に立つ話を交えたいと思いました。また、緊急コラム「 bash 脆弱性( CVE-2014-6271 )の影響範囲の調査方法について」が掲載されました。で「あまりに dis りすぎたため、公開したら怒られそうな内容になってしまいました」と書いたように、一昨年/昨年のテキストについては公開することを躊躇っていましたが、責任をとらないお偉いさんたちが蔓延していく現在の日本の危機的状況を見て、「来年では間に合わない」と判断し、過去テキストも含めて全テキストへのリンクを含めることにしました。

当日学習で使用した VirtualBox 向けのVMイメージは、 http://jaist.dl.sourceforge.jp/caitsith/63583/ からダウンロードできます。(ただし、サイズが大きいので、1か月後くらいに削除するつもりです。テキストでは CentOS 6 / CentOS 7 / Ubuntu 14.04 の3つしか言及していませんが、受講者の中に Arch Linux ユーザが居ましたので、 Arch Linux 用のVMイメージも用意しました。)

キャンプの様子は http://togetter.com/li/859151 から察していただければと思います。他の講師の方々が公開された資料や、参加者の反応なども見つけることができます。

posted by 熊猫さくら at 01:41| Comment(0) | TrackBack(0) | Linux

2015年04月03日

makedumpfile コマンドの罠

Red Hat 社のナレッジ の中に、 vmcore ファイルのファイルサイズを削減するには makedumpfile -d 31 を使ってフィルタリングするようにという記述があります。しかし、 RHEL 6 および RHEL 7.0 に含まれている makedumpfile コマンドには、本来はフィルタリングされるべき Transparent Huge Pages の内容がフィルタリングされないという不具合があります。その結果、 vmcore ファイルのファイルサイズが必要以上に大きくなったり、 vmcore ファイル内に機密情報が残存しやすくなったりします。この不具合は RHEL 7.1 に含まれている kexec-tools パッケージで修正され、 RHEL 6 系についても RHEL 6.7 で修正予定とのことです。

RHEL 6.7 がリリースされるまでの暫定対処としては、サポート対象外の方法ではありますが、 RHEL 7.1 用の kexec-tools パッケージを RHEL 6 環境にインストールして使うことができます。 CentOS 6 環境に CentOS 7.1 用の kexec-tools パッケージをインストールする例を以下に示します。既存の環境を壊さないように、 rpm コマンドの実行時に --root オプションを指定しています。

# source=http://ftp.jaist.ac.jp/pub/Linux/CentOS/7.1.1503/os/x86_64/
# dest=~/kexec-tools/
# rpm --root $dest/ --import $source/RPM-GPG-KEY-CentOS-7
# rpm --root $dest/ --nodeps --noscripts -ivh $source/Packages/kexec-tools-2.0.7-19.el7.x86_64.rpm $source/Packages/snappy-1.1.0-3.el7.x86_64.rpm $source/Packages/lzo-2.06-6.el7_0.2.x86_64.rpm $source/Packages/elfutils-libs-0.160-1.el7.x86_64.rpm $source/Packages/xz-libs-5.1.2-9alpha.el7.x86_64.rpm $source/Packages/glibc-2.17-78.el7.x86_64.rpm

取得済みの vmcore ファイルを再度フィルタリングする際の例を以下に示します。 CentOS 6 用ではなく CentOS 7.1 用のライブラリが参照されるようにするために、 LD_LIBRARY_PATH= オプションの指定と /lib64/ld-2.17.so 経由での起動をしています。

# dest=~/kexec-tools/
# LD_LIBRARY_PATH=$dest/lib64/:$dest/usr/lib64/ $dest/lib64/ld-2.17.so $dest/sbin/makedumpfile -l -d 31 フィルタ前のvmcoreファイル フィルタ後のvmcoreファイル

kexec-tools-2.0.0-280.el6.x86_64 がインストールされている CentOS 6.6 環境に、上記の手順で CentOS 7.1 の kexec-tools-2.0.7-19.el7.x86_64 をインストールし、 メモリ 2048MB 中の 1536MB を特定のパターンで埋め尽くした状態で取得した vmcore ファイルを、上記の手順で再度フィルタリングした場合のサイズの変化例を以下に示します。

使用する
パターン
フィルタオプションvmcore ファイルの
サイズの変化
0-d 31100,739,352

91,312,408
-l -d 3129,346,091

20,415,428
1-d 311,706,537,640

91,736,744
-l -d 3140,430,575

20,533,033
rand() 関数の
戻り値
-d 311,705,867,632

91,050,376
-l -d 311,634,827,177

20,510,161

上記は意図的に特定のパターンで埋め尽くした場合に生じる極端な結果ですが、現実のメモリ使用状況でもある程度の効果を期待できる筈です。

posted by 熊猫さくら at 21:11| Comment(0) | TrackBack(0) | Linux

2015年03月31日

最終回「防災訓練ノススメ」が掲載されました。

今回はトラブルに遭遇する前に何ができるかという話です。

OSSセンタで3年間、問合せ対応を行ってきましたが、「発生したトラブルについての原因と対策を教えてほしい」という問合せはあっても、「トラブルが発生した場合の対処手順を教えてほしい」という問合せはありませんでした。それだけ、「どのようなトラブル発生事例があるのか」や「どのようなトラブルが発生しうるのか」についての共有ができていないということなのだと思います。

でも、「どのようなトラブル発生事例があるのか」や「どのようなトラブルが発生しうるのか」を知っているOSSセンタ側も、「トラブルが発生した場合の対処手順」を用意できているとは言い難い状況でした。熊猫はカーネルの開発経験があるため、カーネルクラッシュダンプの取得に関する問合せや解析依頼に対応しながら、取得手順や初期解析の手順を作成してきました。その第一歩が、今回の話に登場した「ナレッジの泉」に反映されています。もちろん、システムに固有の部分については対応できませんが、共通する部分については「トラブルが発生した場合の対処手順」を考えておくべき時期が来たのではないかと思います。

posted by 熊猫さくら at 22:33| Comment(0) | TrackBack(0) | Linux

2015年03月17日

第17回「プログラミング体験ノススメ」が掲載されました。

今回はプログラムを自作してみようという話です。

「たまゆら」を観ていると「 ARIA 」の世界を思い出してしまうのですが、自分の手でやっている感触というんでしょうかね。熊猫は、そういう癒し系アニメに出会うと好きになってしまいます。提供されたAPIを呼び出して結果を待つだけではなく、「ああでもない、こうでもない」と考えて行動した人だけが辿り着ける「(細かな問題点にも気付く)気配り/思いやりスキル」が存在するのだと思います。

世の中がそういうスキルを持つ人たちで満たされていれば対処する必要のない脆弱性に関して重箱の隅をつつき続けた結果、当初は議論に値しないと乗り気でなかったため「30分で終わる」と思っていたらしい Linux Storage filesystem and MM summit での議論が、2時間くらい続く大炎上となった模様です。熊猫は留守番でしたので内容は知りませんが、 LWN.net の記事によると、 3.19-rc6 後に紛れ込んだ予期せぬ挙動の変化を容認する(ファイルシステムエラーなどが起こらないように呼び出し側を修正していく)方向になったようで、そのためのパッチが LKML に投稿されています。ディストリビューションカーネルのデフォルトの挙動にして、エンドユーザにメモリ枯渇時のカーネルの不具合を見つけてもらうことを期待しているようですが、それは無理すぎる気が・・・。

posted by 熊猫さくら at 21:47| Comment(0) | TrackBack(0) | Linux

2015年03月03日

第16回「 kernel-debug ノススメ」が掲載されました。

今回は デバッグ用カーネルの紹介です。でも、デバッグ用カーネルの紹介だけで1話持たせるのは無理があるので、過負荷試験の話も入れました。

今までは「システムがダウンするくらいにまでメモリ負荷を掛けたら、いつ回復するか予測できないストール状態に陥るのは当然だ。そのような負荷を掛けたユーザのほうが悪い」と相手にされなかったのですが、実は「無限ループに陥るトドメの一撃」を喰らわせていたのはユーザ側ではなくメモリ管理機構側だったようです。
前回紹介したメモリ枯渇時の挙動についての議論が、 3.19-rc6 後に紛れ込んだ予期せぬ挙動の変化をきっかけに、一気に動き出しました。
ext4 でファイルシステムエラーが頻発したり、 xfs でページフォールトするだけで OOM killer が発生したりと、全く使い物にならなくなってしまうことが確認されたため、とりあえずは元の挙動に戻されました
そして、 mm のメンテナと ext4 のメンテナと xfs のメンテナとの間で将来に向けてどう修正していくのかの議論の最中なのですが、熊猫が欲している「既存のカーネルにバックポート可能な修正方法」についての話が完全に置いてけぼり状態になっています。
メモリ割り当て要求が原因のシステムフリーズとさよならできるようになる日が来るのはまだ遠いのかなぁ。
来週、ボストンで Linux Storage filesystem and MM summit という 会議があるので、そこで何らかの進展があることを期待しています。

posted by 熊猫さくら at 20:56| Comment(0) | TrackBack(0) | Linux

2015年02月17日

第15回「フォールトインジェクションノススメ」が掲載されました。

今回は SystemTap を使ってエラーを発生させるというちょっと怖いお話です。でも、ここでは目を離した隙にカーネルの挙動が変化しているという別の方向で怖い話をしたいと思います。

使い方1で紹介した不具合は RHEL 6/7 カーネルにバックポートされましたが、修正されたことを知るのは面倒です。というのも、エラータのページにはセキュリティ上の不具合の修正内容についてしか言及されておらず、「その他の修正内容についてはテクニカルノートを見てね」と書かれているのに従ってテクニカルノートのページをチェックしても、今回の不具合に該当しそうな内容が見当たりません。実は、 rpm パッケージの変更履歴の説明文をチェックするかソースコードの差分をチェックして初めて、「あ、修正されたんだな」と判るようになっていたのです。テクニカルノートには全修正内容が網羅されていると思っていると、落し穴にはまるかもしれないということですね。

使い方2で紹介した挙動は、 3.19-rc6 がリリース(1/25)されてから 3.19 がリリース(2/8)されるまでの僅かな間に、 __GFP_FS が含まれていない場合にはメモリ枯渇時に OOM Killer を発動させずに諦めるという変更が適用されてしまいました。 linux-next.git でテストを行って問題が無いことを確認してから linux.git の -rc1 がリリースされるまでにマージするというのが本来の手順なのですが、その手順をすっとばされた訳です。ファイルシステムより低いレイヤーでのメモリ割り当てがメモリ枯渇時に失敗してしまうようだと、ファイルシステムエラーが多発してしまうので困ると思うのですが。そういう変更を適用するのなら、 __GFP_NOFAIL が必要な個所を全て洗い出して修正してからにしてほしかったんだけどなぁ。

posted by 熊猫さくら at 22:56| Comment(0) | TrackBack(0) | Linux

2015年02月03日

緊急コラム「 glibc 脆弱性( CVE-2015-0235 )の影響範囲の調査方法について」が掲載されました。

ルイージ@お化け屋敷のような、次から次へと襲い掛かってくる問合せの嵐に困っています。

glibc の不具合や脆弱性が見つかる度に、影響を受けるプログラムの調査方法を説明するというのも馬鹿げた話ですので、今回の glibc 脆弱性を機に、緊急コラムとして調査方法を説明することにしました。
一昨日考えた内容を急いでダンプしたものなので、全てを網羅できている訳ではありません。でも、影響範囲を調査することの難しさに気付いて素直にアップデートをしていただけるのなら、この記事を作成した意味はあると考えています。

今回の脆弱性については幽霊に振り回されすぎている感がありますが、システムの動作を再確認してみるチャンスかもしれません。任意のホスト名を渡すことができるかどうかをソースコードレベルで網羅的に調査してみたら、OSコマンドインジェクションのような、今まで見落としていた脆弱性が見つかってもおかしくないと思っています。

posted by 熊猫さくら at 21:15| Comment(0) | TrackBack(0) | Linux

2015年02月02日

第14回「 SystemTap ノススメ」が掲載されました。

今回は SystemTap を使って挙動を追跡するというお話です。

とあるシステムで、 Pacemaker を起動してから約70秒後にシステムが予期せぬ再起動をしてしまうというトラブルが発生していました。再起動する直前だけディスクI/Oが多いという傾向が見られたため、何らかのディスクI/Oに関するトラブルに巻き込まれた結果、 /dev/watchdog への書き込みが遅延して watchdog タイムアウトによるリセットが発生しているのではないかと疑ったのですが、原因を掴めませんでした。

そこで、再起動要求が発生した時点の kdump を取得することにより、誰が再起動要求を発生させているのか/どうして再起動要求を発生させることになったのかを調査するために、今回紹介した SystemTap スクリプトを仕掛けて事象を再現してもらいました。すると、何と sfex_daemon という監視プログラムが /proc/sysrq-trigger に b を書き込んだことによるリセットが発生していたことが判明したのです。これをきっかけに、設定の誤りが予期せぬ再起動の原因であったことが判明し、トラブルを解決することができました。

sfex_daemon のソースコードによると、普段はスリープしながら、定期的に update_lock() を呼び出してロックの状態を確認し、異常を検知した場合には failure_todo() を呼び出しているようです。 failure_todo() は cl_log() という関数の中から syslog() 関数を呼び出すことで syslog デーモンにログメッセージを送信後、 /proc/sysrq-trigger に b を書き込んでいるようです。では、何故 syslog() 関数を介して送信した筈のログメッセージが、 syslog ファイルに残っていなかったのでしょうか?実は、 syslog() 関数を介して syslog デーモンにログメッセージを出力する方法には、2つの落し穴があったのです。

1つ目の落し穴は、 rsyslog の version 3 からは、デフォルトで非同期書き込みを行うようになっている点です。そのため、非同期書き込み後すぐに再起動要求が発生してしまっては、ログに残すことができません。2つ目の落し穴は、 syslog() 関数を介して syslog デーモンにログメッセージを出力する方法では、 syslog デーモンがログメッセージを出力するよりも前に syslog() 関数が復帰してしまうため、たとえ同期書き込みを行うようになっていたとしても、同期書き込みの完了を待たずに再起動要求が発生してしまい、ログに残すことができません。

RHEL 7 の登場により、3つ目の落し穴も発見されました。 RHEL 7 の場合はデフォルトのファイルシステムが ext4 から xfs に変更されたため、 ext 系ファイルシステムで行われていた5秒毎の writeback も行われなくなっています。そのため、 RHEL 7 においては、明示的に sync() 関数などを呼ばないと、問題事象発生直前のログが残っていないという可能性が一層高くなっています。

再起動直前のログを確実に残したい場合、 syslog デーモンに頼っていては駄目なのかもしれません。今後 RHEL 7 のシステムが増えてくるに従い、問題事象発生時のログを如何に残すかという課題が出てきそうです。カーネルメッセージであればシリアルコンソールや netconsole 経由で取得するという方法が使えます。アプリケーションのメッセージについても自力でローカルとリモートの両方にログを出力するとかしたほうが良いのかなぁ?

明日は節分ですって? 幽霊は〜外〜。アップデートは〜内〜。(謎)

posted by 熊猫さくら at 22:00| Comment(0) | TrackBack(0) | Linux

2015年01月20日

第13回「 TaskTracker ノススメ」が掲載されました。

今回は Hadoop のお話・・・ではなくて、緊急コラム「 bash 脆弱性( CVE-2014-6271 )の影響範囲の調査方法について」の中で紹介していた System Call Auditing のログの中に TOMOYO Linux 風のプロセス実行履歴を埋め込むというお話です。
メインラインカーネルへのマージを目指してMLに提案中ですので、コードの内容が若干変化しています。興味のある方は、MLでの議論に参加してコメントを頂けると嬉しいです。

posted by 熊猫さくら at 22:42| Comment(0) | TrackBack(0) | Linux

2015年01月06日

第12回「 System Call Auditing ノススメ」が掲載されました。

今回は犯人探しのお話です。セキュリティ目的で使う場合には SELinux などと併用して監査ログの改ざんなどをされないようにする必要がありますが、デバッグ目的で使う場合には Red Hat 社のサポートが受けられる標準機能なので気軽に試せるかと思います。

熊猫はコーヒーブレークの内容にはノータッチなのですが、今回のコーヒーブレークの内容には突っ込みを入れておこうと思います。
問題を解決するのに必要な情報が提供されない理由のひとつに、「サポート技術者が何をもとに調査や解析を行うかがあまり知られていない」ことにあるという点は同意します。しかし、問題が起きたときに、「どんな問題が起きているのか判らない」「どこに必要な情報が保存されているのか判らない」「どうすれば必要な情報を取得できるのか判らない」「トラブルの発生後に情報取得のツールやコマンドを提示されても、すぐに使いこなせるとは限らない」など、「製品別に調査に必要な情報の一覧を作っておくだけでは対処できない」部分も多いと思うのです。
OSSであってもシステムの安定稼働とセキュリティが年々重要視されてきている中で、お客様がトラブルに遭遇したときに自力で情報収集や一次切り分けができるスキルをどうやって共有/継承していくのかという「教育」の問題でもあり、問題解決のお手伝いをする組織としてどう対処していくのかを考え続けなければいけない課題でもあると思うのです。熊猫が安心して年次有給休暇を取得できるようになるためにも、ね。

posted by 熊猫さくら at 22:02| Comment(0) | TrackBack(0) | Linux

2014年12月24日

第11回「 strace ノススメ」が掲載されました。

今回は誰かの行動を一つ残らず追いかけるという話です。
知世ちゃんのことを思い出したので、「歓びのキャロル」を聞きながらこちらの補足記事を書いています。

strace は普通に使われていると思うので、特別な内容は無いです。ログを頼りに問題事象の解析を行う調査担当者の立場としては、「事象の再現手順を確立する」「事象の発生時刻が判るようにする」という基本事項の繰り返しです。

今年もいろいろなバグや脆弱性に振り回され続けてきました。熊猫はというと、昨年偶然発見した「 Linux 2.0 (18年前!)から存在している(と考えられる)脆弱性」により引き起こされる様々な問題事象に関して、脆弱性の修正方法を議論して修正内容がバックポートされるまでの間ずっと無防備になることを避けるために、いくつかの問題事象を先に修正してもらえるよう試行錯誤していました。
そして、問題事象の1つである、 OOM killer の発生時に deadlock してしまう事象に関して、脆弱性を使わずに事象を再現させるプログラムを作成して投稿したところ、関係者からのコメントを頂くことができ、衝撃の事実が明らかになりました。
「システムの所有者は Linux から BSD への乗り換えを真剣に検討し始めるだろう」という冗談が飛び出すような、とんでもない相違が存在していたのです。
これじゃあ、システムがロックアップしてしまうのも不思議ではないですねぇ。どうやって修正するのか、これから延々と議論が続くことになりそうです。

ヤマノススメ セカンドシーズン、素敵なアニメでした。サードシーズンはいつ出るのかなぁ?

posted by 熊猫さくら at 22:29| Comment(0) | TrackBack(0) | Linux

2014年12月09日

第10回「ソースコード閲覧ノススメ」が掲載されました。

今回はディストリビューションに同梱されているプログラムのソースコードの確認手順の話です。各自の環境で試せるように、 CentOS ユーザ向けの手順にしてあります。

何か問題が発生した時に、ちょっとソースコードを確認すれば、どこで問題が起きているのか/起こりそうかが判明することが少なからずあります。また、ソースコードを確認できるようになっていれば、 SystemTap などの解析ツールを使用することで原因を突き止めやすくなります。

商用のウイルス対策ソフトや死活監視プログラムのように、ディストリビューションに同梱されていないプログラムを使用している環境では、普段遭遇しないようなエラーコードが返却されたり、フリーズしたり再起動したりするトラブル事例が多いと感じています。しかし、残念ながらソースコードを確認できないため、原因不明のまま諦めざるを得ません。各種ウイルス対策ソフトが使っている(リアルタイム検索のための)カーネルモジュール部分のソースコードを閲覧できるようになっていれば、だいぶ状況は良くなりそうなのになぁ・・・。

posted by 熊猫さくら at 21:27| Comment(0) | TrackBack(0) | Linux

2014年11月25日

第9回「アップデートノススメ」が掲載されました。

今回は RHEL におけるソフトウェアの管理の話です。

この連載のタイトルは「ヤマノススメ」に倣っているので、企画段階では今回は rpm パッケージを管理するための yum コマンドの使い方を紹介する「 yum ノススメ」となる予定でした。

しかし、今年も大型脆弱性が続々と発見されているのに、相変わらず「アップデートしないで済む根拠」を確認するための問合せが目立っており、どうも「アップデートすることを必要以上に恐れすぎているのではないか」と思えるのです。「リグレッションに遭遇して動かなくなるのが怖い」のか「個々のプログラムや設定がどこでどのように影響しあっているかを知らないから怖い」のかは判りません。前者なら変更履歴とソースコードとの照合を行うとか、後者なら自分が管理しているシステムについて理解しようと努力するとか、塩漬けにする以外に何かできることはあると思います。オープンソースを使ったシステムがブラックボックスのままだなんて、モッタイナイ。

posted by 熊猫さくら at 21:15| Comment(0) | TrackBack(0) | Linux

2014年11月11日

第8回「カーネルパラメーターチューニングノススメ」が掲載されました。

今回はカーネルパラメータチューニングです。

予期せぬ再起動や系切り替えが発生する前に何とかして手がかりを得ようと試行錯誤を続けた結果として「エンタープライズ向けサーバのトラブル対応のための情報取得方法について」という資料ができました。そして、その後も試行錯誤を続けた結果、今度は SystemTap が役に立つかもしれないということに気が付きました。

# stap -g -e '
function call_panic() %{ panic("Calling panic() due to machine restart\n"); %}
probe kernel.function("machine_emergency_restart") { call_panic(); }
probe kernel.function("machine_restart") { call_panic(); }' &

SystemTap を用いて行っていることは簡単で、 /sbin/reboot -f であろうと Watchdog のタイムアウトであろうと echo b > /proc/sysrq-trigger であろうと、システムを再起動するための関数呼び出しが発生したらカーネルパニックを発生させます。カーネルパニックを発生させることができれば、 kdump を取得して解析することができる筈ですから。
ただし、残念ながらトリプルフォールトのように SystemTap を仕掛けられないケースもあります。また、熊猫自身は問題事象の発生する環境を持ち合わせていないため、どれくらい効果があるのかは不明です。

posted by 熊猫さくら at 20:48| Comment(0) | TrackBack(0) | Linux

2014年10月28日

第7回「 sosreport ノススメ」が掲載されました。

今回は sosreport の紹介です。主に、システム構成の把握/ログの確認/リソース使用状況の確認のために取得していただいている訳ですが、いろいろと落とし穴があります。勝手にカーネルモジュールをロードしてしまうとか、大量のCPU時間を消費したりとかディスクI/Oを発生させたりとかの話は本文を参照していただくとして、ここでは調査担当者としての苦労話を書こうかと思います。

リソース使用状況の取得に sysstat ではなく商用の運用監視ツールを使っているために、計算式がどうなっているのかとかグラフをどう見ればよいのかとかが不明というケースがあります。サポート対象外の製品については OSS センタから当該製品のサポート窓口に照会する術を持ち合わせていないため、ログやリソース使用状況の確認ができませんので、お客様のほうから当該製品のサポート窓口に照会していただくことになります。

そうそう、計算式といえば、回収可能なメモリを加味した空きメモリの計算方法というのは長年の課題なんですよね。 RHEL 6.6 カーネルの /proc/meminfo には RHEL 7.0 カーネルに存在している MemAvailable: という項目がバックポートされており、 /proc/sys/vm/meminfo_legacy_layout に 0 を書き込むことで表示されるようになります。この項目があると、空きメモリの監視が格段に楽になるのではないかと思います。

で、話を元に戻しますと、他には、 sysstat を使っているけれども、取得したデータがすぐにログサーバに移動されてしまうために /var/log/sa/ 配下には保存されておらず、せっかく sosreport を取得していただいたのに、リソース使用状況を確認できないというケースもあります。しかし、 sosreport の取得をお願いする時点では、 sysstat の使用有無やログの保存場所がどこに設定されているのかは不明な訳です。そして、お客様のサーバにログインして構成を調査をすることはできませんので、お客様のサーバ向けにカスタマイズしたログの取得手順を提示することもできない訳です。

ですので、今回の記事を読んで、ログファイルがどこに保存されているかをトラブルに遭遇する前に把握して、 sosreport には含まれていない必要なログファイルを一緒に取得していただけると有難いなぁと思っている訳です。

え?ログファイルがどこに保存されているかを知るためのツールは無いのかって?

それなら、緊急コラム「 bash 脆弱性( CVE-2014-6271 )の影響範囲の調査方法について」で紹介した TOMOYO や AKARI の出番ですね。

posted by 熊猫さくら at 21:12| Comment(0) | TrackBack(0) | Linux

2014年10月14日

第6回「 Bugzilla ノススメ」が掲載されました。

今回は Bugzilla の紹介です。ここでは、「再現手順を確立することの重要さ」の例として、熊猫が先月対応していた不具合事例について紹介したいと思います。

この不具合は以下のような経緯により原因が特定され、修正されました。現在は RHEL 6/7 カーネルにバックポートされるのを待っている状態です。

  1. Red Hat 社がカーネルパニック発生時点での解析結果と矛盾点を公開( https://access.redhat.com/solutions/640843 )していた。
  2. お客様は原因不明のカーネルパニックに悩まされており、( HP 社サーバと一緒に購入した OEM 版の RHEL サブスクリプションを使用していたため)サポート窓口となる HP 社に照会を行い、上記の不具合情報の存在について把握した。
  3. お客様は NTT OSS センタにも照会を行い、再現手順を確立できないと先に進めないことを理解した。そして、お客様自身が cgroup を使用していると発生頻度が高くなることを突き止め、問題事象を再現できる手順を確立した。
  4. OSS センタでは、上記の矛盾点が発生しそうな箇所で整合性検査を行う SystemTap スクリプトを作成し、 SystemTap を用いた調査を提案した。
  5. お客様は SystemTap を用いた情報取得を行い、カーネルパニックに至るシグナルを受信するよりも前に矛盾点が発生していることを突きとめることができた。
  6. SystemTap で取得した情報を基に OSS センタで解析し、 cgroup に含まれるスレッドセーフでは無い処理が問題事象の発生原因であることを突きとめることができた。
  7. コミュニティに報告し、問題が修正( https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2ad654bc5e2b211e92f66da1d819e47d79a866f0 )された。

この不具合は5年前に公開された Linux 2.6.31 で混入し、不整合状態の発生条件が命令の実行タイミングに強く依存していたことと、不整合状態が発生してからシグナルを受信するまでの処理の流れを kdump の情報から追うことができないケースという特殊さが組み合わさって、不明瞭なカーネルパニックを引き起こしてきたようです。問題事象が発生する環境と同一のハードウェアやシステム構成を持ち合わせていない OSS センタでも問題事象を再現させることができず、お客様自身が問題事象の再現手順を確立する以外に先に進めないという状況にありました。そして、関係各位の連携プレーにより、見事解決することができました。

問題解決までの流れを知っておくと、サポートを利用する側としてどのようなことができるかを考えることができるようになります。スムーズな問題解決のために、ご協力をお願いいたします。

posted by 熊猫さくら at 20:13| Comment(0) | TrackBack(0) | Linux

2014年10月01日

bash 脆弱性( CVE-2014-6271 )の回避策として LD_PRELOAD 環境変数を使うことの罠について

サポートケースに投げても customerservice 宛に投げても secalert 宛に投げても反応が無いので、ここで説明することにします。( 10/2 追記: secalert の中の人から反応がありました。スパムキューの中に埋もれていて気付いていなかったそうです。 10/7 追記: PassEnv LD_PRELOAD を追加するように記述が修正されました。)

https://access.redhat.com/articles/1212303 には、「 /lib/bash_ld_preload.so による回避策は潜在的に危険であるため、(システム全体に適用されてしまう) /etc/ld.so.preload にではなく(サービス単位での適用が可能な) init スクリプトでの LD_PRELOAD 環境変数の設定を推奨する」という記述があり、一例として httpd サービスに適用する手順が紹介されています。

If you wish to apply this workaround across the entire system:

  • Add the following to /etc/ld.so.preload on a line by itself:
/lib/bash_ld_preload.so
  • Restart all relevant services or reboot the system.

Note that this is potentially very dangerous. It is recommend that you just apply this workaround to specific services that may be exploitable on your system. This can be achieved by adding bash_ld_preload.so to the LD_PRELOAD environment variable in the script that will initialize the service. For example, for httpd on Red Hat Enterprise Linux 6:

  • Add the following two lines at the top of /etc/init.d/httpd, after the shebang line:
LD_PRELOAD=/lib/bash_ld_preload.so
export LD_PRELOAD
  • Then restart httpd:
# service httpd restart

しかし、上記のように LD_PRELOAD 環境変数を設定しても、効果はありません。効果が無いことを確認する手順を以下に説明します。

C言語で作成されたCGIプログラムを使って確かめてみましょう。

---------- Source code of /var/www/cgi-bin/test.cgi start ----------
#include <stdio.h>

int main(int argc, char *argv[], char *environ[]) {
  int i;
  printf("Status: 200\r\n");
  printf("Content-type: text/plain\r\n");
  printf("\r\n");
  for (i = 0; environ[i]; i++)
    printf("%s\n", environ[i]);
  return 0;
}
---------- Source code of /var/www/cgi-bin/test.cgi end ----------

このCGIを叩いてみましょう。

# curl -H 'User-agent: foobar' http://127.0.0.1/cgi-bin/test.cgi

HTTP_USER_AGENT=foobar という行は表示されるのに対して、 LD_PRELOAD=/lib/bash_ld_preload.so という行は表示されないことが確認できます。言い換えると、 LD_PRELOAD 環境変数は /usr/sbin/httpd プロセスには継承されていますが、 /usr/sbin/httpd プロセスから起動されるCGIプログラムには継承されていないことを意味しています。

そのため、 /etc/init.d/httpd の中で LD_PRELOAD 環境変数を設定する方法では、 以下のように /bin/bash を呼び出す可能性のあるCGIプログラムを保護することはできません。

---------- Content of /var/www/cgi-bin/test1.cgi start ----------
#!/bin/sh
echo "Status: 200"
echo "Content-type: text/plain"
echo ""
exec /bin/env
---------- Content of /var/www/cgi-bin/test1.cgi end ----------
---------- Source code of /var/www/cgi-bin/test2.cgi start ----------
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
  printf("Status: 200\r\n");
  printf("Content-type: text/plain\r\n");
  printf("\r\n");
  fflush(stdout);
  system("/bin/env");
  return 0;
}
---------- Source code of /var/www/cgi-bin/test2.cgi end ----------

以下のような叩き方をすれば、 /tmp/file を作成できてしまう訳です。

# curl -H 'User-agent: () { :;}; exec /bin/touch /tmp/file' http://127.0.0.1/cgi-bin/test1.cgi
# curl -H 'User-agent: () { :;}; exec /bin/touch /tmp/file' http://127.0.0.1/cgi-bin/test2.cgi

環境変数が意図した範囲に継承されているかどうかを理解しないまま、 LD_PRELOAD 環境変数による回避策を使おうとするのは危険です。

posted by 熊猫さくら at 21:59| Comment(0) | TrackBack(0) | Linux