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