2016年12月3日

元号の処理がどこで行われているか探してみる

この記事はLinux Advent Calendar 2016の4日目の記事です。

わりとマイナーな機能ですが、linuxでja_JP.utf8のlocaleで以下のようにコマンドを叩くと、元号をちゃんと処理して表示してくれたりします。
$ LC_TIME=C date +'%EY'
2016
$ LC_TIME=ja_JP.utf8 date +'%EY'
平成28年
ちょっと前に国の象徴のおじいさん(82)が「退位したい」みたいなこと言ってましたし、いいかげん年なのでしんどかろうと思います。法律の要件とかはわかりませんが退位が行われれば元号が変わるでしょうから、庶民の我々も少なくともこのコードを修正しないといけないわけです。 

これはどこからきているのかを探りましょう。
 $ ldd `which date`
    linux-vdso.so.1 (0x00007ffd14593000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fea550ed000)
    /lib64/ld-linux-x86-64.so.2 (0x000056070c37e000)
シンプル。linux kernel, dateコマンドの中身, libcのどれかに絞られました。
kernelはありえないので候補から外します。心配な人はstraceでもして安心してください。
date本体もないと思っているのですが念の為確認します。
$ LC_TIME=ja_JP.utf8 ltrace date +'%EY'
(中略)
strftime(" \345\271\263\346\210\22028\345\271\264", 1024, " %EY", 0x7f8f5c1464a0)                                                   = 12
fwrite("\345\271\263\346\210\22028\345\271\264", 11, 1, 0x7f8f5c142600)                                                             = 1
(以下略)
libcのstrftimeがやっているようです。man 3 strftime を見ます。
(ー略ー)
        %E     Modifier: use alternative format, see below. (SU)
(ー略ー)
(SU) The Single UNIX Specification mentions %Ec, %EC, %Ex, %EX, 
       %Ey,  %EY,  %Od,  %Oe, %OH, %OI, %Om, %OM, %OS, %Ou,
       %OU, %OV, %Ow, %OW, %Oy, where the effect of the O modifier
       is to use alternative  numeric  symbols  (say,  roman numerals),  and 
       that  of  the  E modifier is to use a locale-dependent alternative
       representation.
あ、Single UNIX Specificationで決まってたのか。ってことはきっとAppleのOS Xとかでもつかえるんだろうな。。持ってないので知らないけど。

さて、libcでlocale-dependentだということがわかりました。ソースコードとってきましょう。
$ find glibc-2.24 |grep ja
./localedata/locales/ja_JP
./benchtests/strcoll-inputs/lorem_ipsum#ja_JP.UTF-8
./po/ja.po
./debian/po/ja.po
ということで以下のファイルに含まれてそうです。
glibc-2.24/localedata/locales/ja_JP
いかにもそれらしい「era」というエントリがあります。きっとこれなんですが……

era     "<U002B><U003A><U0032><U003A><U0031><U0039><U0039><U0030><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U002B><U002A><U003A><U5E73><U6210><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
        "<U002B><U003A><U0031><U003A><U0031><U0039><U0038><U0039><U002F><U0030><U0031><U002F><U0030><U0038><U003A><U0031><U0039><U0038><U0039><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U5E73><U6210><U003A><U0025><U0045><U0043><U5143><U5E74>";/
        "<U002B><U003A><U0032><U003A><U0031><U0039><U0032><U0037><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0038><U0039><U002F><U0030><U0031><U002F><U0030><U0037><U003A><U662D><U548C><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
        "<U002B><U003A><U0031><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0032><U0035><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U662D><U548C><U003A><U0025><U0045><U0043><U5143><U5E74>";/
        "<U002B><U003A><U0032><U003A><U0031><U0039><U0031><U0033><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0032><U0036><U002F><U0031><U0032><U002F><U0032><U0034><U003A><U5927><U6B63><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
        "<U002B><U003A><U0032><U003A><U0031><U0039><U0031><U0032><U002F><U0030><U0037><U002F><U0033><U0030><U003A><U0031><U0039><U0031><U0032><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U5927><U6B63><U003A><U0025><U0045><U0043><U5143><U5E74>";/
        "<U002B><U003A><U0036><U003A><U0031><U0038><U0037><U0033><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0039><U0031><U0032><U002F><U0030><U0037><U002F><U0032><U0039><U003A><U660E><U6CBB><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
        "<U002B><U003A><U0031><U003A><U0030><U0030><U0030><U0031><U002F><U0030><U0031><U002F><U0030><U0031><U003A><U0031><U0038><U0037><U0032><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U897F><U66A6><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>";/
        "<U002B><U003A><U0031><U003A><U002D><U0030><U0030><U0030><U0031><U002F><U0031><U0032><U002F><U0033><U0031><U003A><U002D><U002A><U003A><U7D00><U5143><U524D><U003A><U0025><U0045><U0043><U0025><U0045><U0079><U5E74>"

ソースコードのくせに謎エンコードされてて読めないよ! >_<

きっとunicodeの表が頭に全部はいっててデコードできる人は心の目で読めるんですがそんなハンドアセンブルの100倍くらいしんどそうなスキルは持っていません。ちなみにハンドアセンブルも表がないとできません。

しょうがないので<Uxxxx>; みたいなのが来たらxxxx部分にあたるunicode文字に置換する使い捨てスクリプトをシコシコ書きます。

import os, re

def main():
    for line in file('ja_JP'):
        out = ""
        while 1:
            m = re.search('(<U([0-9a-fA-F]+)>;?)', line)
            if m:
                out = out + line[:m.start()] + unichr(int(m.group(2), base=16))
                line = line[m.end():]
            else:
                out = out + line
                break
        print out.encode('utf8'),

main()
 これを実行すると.. でました
era     "+:2:1990/01/01:+*:平成:%EC%Ey年";/
        "+:1:1989/01/08:1989/12/31:平成:%EC元年";/
        "+:2:1927/01/01:1989/01/07:昭和:%EC%Ey年";/
        "+:1:1926/12/25:1926/12/31:昭和:%EC元年";/
        "+:2:1913/01/01:1926/12/24:大正:%EC%Ey年";/
        "+:2:1912/07/30:1912/12/31:大正:%EC元年";/
        "+:6:1873/01/01:1912/07/29:明治:%EC%Ey年";/
        "+:1:0001/01/01:1872/12/31:西暦:%EC%Ey年";/
        "+:1:-0001/12/31:-*:紀元前:%EC%Ey年"
元年だけ別の行になってるんですね…… 明治は6年からなので旧暦のごちゃっとしたあたりは気にしないと。

というわけで元号の場所はわかりました。おじいさんが退位したらここを直してみんなでglibcをアップデートしよう。

ではでは。


追記

「別ファイルになってないの?」と聞かれたので追記 。
たとえば /usr/share/i18n/locales/ja_JP のように別ファイルになっていて、localedefを使うことで独自のlocaleを定義することもできる。
でもlocaleの設定がおこなわれるのはプロセスの最初のほうで setlocale() されるタイミングだから結局元号の処理更新を反映したいプロセスの再起動が必要なのは変わらないのでした……。

2016年9月27日

srad.slack.com 利用案内


スラドのIRCをもうちょっとモバイルにやさしいところでやりたい(?)的なことでslackに挑戦中。

参加方法について

Google Formの参加用フォームにメールアドレスを入れると参加できます: https://goo.gl/forms/SLWmqyldlgvbvNGN2
※入力したメールアドレスは参加者に見えてしまうので気になる人は捨てフリーメール使ってください

チャンネルについて

メインで使うのは 
 #announce アナウンス用チャンネル
 #random 雑談チャンネル
 #relay-test IRCnetの#すらっしゅどっと と相互リレーしているチャンネル
他のチャンネルを作るのは自由にどうぞ。

クライアントについて

slackにはWebやslack専用クライアントを利用して接続してもいいけどIRCクライアントやXMPPに対応した汎用のメッセンジャーアプリも使えます。

IRC, XMPPクライアントからの繋ぎ方は https://srad.slack.com/account/gateways
IRC用とXMPP用の設定と専用パスワードとかが各個人用に生成されて記載されてる。
IRCの文字コードはUTF-8

2016年6月3日

Red Hat SatelliteとRed Hat Insights は何が違うか

全然違うのだけれど混乱してる人がいたので……

何が目的か:
・Red Hat Satellite → パッケージと設定の管理を中心としたライフサイクル管理システム
・Red Hat Insights → プロアクティブな問題対応を目的としたレポート生成システム

何ができるか:
・Satellite → パッケージと設定の配布を含む実際の操作。auditを考慮したロギングなど履歴の管理。
・Insights → レポートをだすだけ。現在の最新状況だけが見える。

何を見るか:
・Satellite → 基本的にパッケージと設定のバージョンを管理
・Insights → パッケージのバージョンだけでなくディスク残容量や設定内容、ファームウェアやCPUの型番なども見る

どこで動くか
・Satellite → オンプレ
・Insights → SaaS

ライセンス
・Satellite → OSS
・Insights → 解析エンジンはSaaSなのでそもそも配布していない

2016年2月15日

OOM Killerにであったら何をするべきか?

OOM killerで大事なプロセスが殺される。困りますね。。 google で OOM Killerと入力すると 「無効」とか補完されます。しかしどうするのが良いのか、あまりよく説明されている記事がみあたらなかったので自分の考えをメモしておきます。

OOM Killer の目的は何か?

まずは何故OOM Killerが発生しているのかについて、ざっくりイメージをつかみましょう。linux kernelはプロセスからの「メモリくれ」という要求に対してたぶん足りそうだという場合に「OK」といって渡します。実際のメモリ割り当てはアクセスが発生するタイミングまで遅延させます。これを遅延アロケーションといい、だいたいにおいてうまく動きます。ただし必ずうまくいくと保証されているわけではないので破綻することがあります。

OOM Killerはこの遅延アロケーションが破綻しそうなときに、適当にプロセスを殺すことでメモリを開放してやろう、そうやってメモリを開けて動作を継続したほうが全滅するよりはマシだろうという緩和策です。
「半端に動くんじゃなくて死んでくれた方がいいです」という場合には、 sysctl で以下のように設定して、OOM Killerを動作させるのではなくカーネルをpanicさせることができます。
vm.panic_on_oom = 1
OOM Killerは破綻したときの緩和策だったので、OOM Killerを止めて破綻をより直接的にシステム停止に繋げています。きっちり死んでくれるのでOOM Killerよりわかりやすいですね。

何故OOM Killerが発生しているかを調査する

できればOOM Killerを避けたいですが、避けるためには何故OOM Killerが発生しているのかを調査する必要があります。これをおこなうためには、まずさきほどのpanic_on_oom設定をおこないます。

加えてpanic時にメモリダンプを取得する仕組み(いろいろありますがRHELやFedoraだとkdump)を仕込みましょう。これでpanicしたときのダンプを調査することで、多くの場合OOM Killerの発生原因を推定できます。

RHELであれば「カーネルクラッシュダンプガイド」にダンプの設定と解析ツールについての解説があります。

https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Kernel_Crash_Dump_Guide/index.html

対処その1. アプリケーションに対してストレージを速くする

メモリにはざっくり3種類あります。
  • ファイルやブロックデバイスをバックエンドとするfile mapped page
  • バックエンドはなく、必要になるとswap領域にバックエンドを作るanonymous page
  • 特に使われていないfree
linuxでは基本的にはディスクやファイル、swap領域に対するキャッシュとしてメモリは扱われます。そうではなくメモリ上に必ず維持されるようにする仕組み(mlock)もありますがそれが気になる人はこんなblog読まなくて大丈夫なはずなので割愛します。

アプリケーションが何かしらストレージへ書きだすとき、まずは書き出すべき情報をメモリ上に保持します。このようなメモリをDirtyと呼びます。
Dirtyなメモリはストレージへの反映による永続化が必要な情報を持っているので、ストレージに書きだしをおこなうまでは消せません。恒常的にストレージの負荷が高いとDirtyなメモリがどんどん増えていきます。

さきほどのメモリダンプを解析ツールに食わせると、dirtyなページが多くなっているのですぐわかります。このような場合、対処としては以下があります
  • ストレージの障害をチェックする。何かデグレードが発生して遅くなっている可能性を排除しましょう。 高いストレージだとストレージ側で統計情報がとれます。これも確認しましょう。
  • ストレージを増強する。根本対策です。
  • アプリケーションの書きだしを遅くする。dirtyなページを書きだすプロセスの実行を遅くします。
    アプリケーションがdirect I/Oとよばれる手法を利用している場合に限られますが、cgroupで制限することができます。systemdのunit内にBlockIOWriteBandWidth=で利用する帯域幅の上限を設定することができます。指定された上限にを越えて書きこみをしようとするとアプリケーションは一時停止します。
    https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/sec-Modifying_Control_Groups.html#sec-Modifying_Unit_Files
  • 書きだしを遅くする(その2)。共有ストレージを持つクラスタ構成の場合、ストレージに対しての計算ノード数を減らします。
  • 書きだしを遅くする(その3)。メモリがでかいときはvm.dirty_ratioを変更したりすると良いケースもあります。これはdirtyなページが何パーセント溜まったら、新規に書きだしを行おうとしているプロセスを一時停止するかの指標です。
  • メモリを増やす。ストレージへの負荷が適度に増減する場合、次に負荷が減るまでなんとか持てばいいので、その余裕がある時間をかせぎます。場合によりますが、メモリがとても少ない場合をのぞいてあまり有効な対応ではありません。

対処その2. 積極的にswapさせる

利用しているアプリケーションのメモリ管理があまりうまくできていないと、実際の利用はそれほどでもないのにanonymousとして確保するメモリ量がじわじわ増大していきます。このような場合、アプリケーションによりますがswapを積極的に行うことも一つの手です。

 sysctlで、以下のようにするとRHELではfile mappedと同じ程度の基準でanonymousメモリをswapに吐きだします。
vm.swappiness = 100
 ゆっくりとしたメモリ消費増加への対策としてはswapはそれなりに有効です。もちろんswapがあふれると破綻しますから適宜swap使用量を監視する必要があります。

swapについてはとにかく発生させたくないと、拒否反応を示す人がいます。
「swapで遅くなる」という現象のほとんどは、1) anonymous pageを一旦swapへ書きだしてメモリから除いたあと、2) もう一度アクセスしようとしたときに発生します。
この2の動作をswap inと呼びます。vmstatだとsiと書いてある欄で表示されます。これが継続的に発生する場合、swapによるパフォーマンス劣化が発生していると考えてまず間違いありません。この場合、アプリケーションのメモリ利用パターンがswapの積極的な利用に向いていません。

swap inによるパフォーマンス劣化が継続的ではなくちょこちょこ程度で起きる場合、主要ではないアプリケーションがswap outしている可能性が考えられます。この影響を緩和したい場合「メモリを増設するのは高いのでswap領域をSSDにしてしのぐ」というアイデアには一考の余地があります。

利用しているアプリケーションに、ある程度予想がつくゆっくりとしたメモリ利用の増加がある場合にはswapと適当な頻度でのサービス再起動の組み合わせは有効です。ただしswap inが継続的に発生する場合はswapによる緩和策はあまり向いていません。

対処その3. アプリケーションのメモリ消費を制限する

swapが向いていないアプリや、メモリの大量消費が突発的に発生するアプリケーションもあります。後者は70%くらいバグな気がしますが、残念ながらそういうアプリケーションでも使わないといけないシチュエーションはあります。

そのような場合、cgroupを利用してアプリケーションのメモリ消費を制限することが有効です。systemdを利用しているディストリビューションでは、systemdがcgroupの設定を管理します。 RHELであれば以下に和訳ドキュメントがあるので読みましょう。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/index.html 

てっとり早く答えにたどりつきたい人は以下ページを見て、MemoryLimitを設定しましょう。 これで、指定したサービスの消費メモリを制限できます。制限に触れるとOOM Killerが動作しますが、このcgroup内のプロセスだけを殺しますのでサービスだけが死にます。周りにあまり迷惑がかからないのはいいですね。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Resource_Management_Guide/sec-Modifying_Control_Groups.html#sec-Setting_Parameters_from_the_Command-Line_Interface


これに加えて、サービス停止時の再起動設定もsystemdのunitに書くことができます。ただし自動再起動の設定をするのは「大量消費→OOM Killer→再起動→大量消費→……」 のタイトなループに陥る可能性が排除できている場合に限定しましょう。このループが数日や数ヶ月など、許容できる場合なら
Restart=yes  
とunitに書くことでsystemdが再起動の面倒もみてくれます。

対処その4. メモリを増やす 

「さっきあまり有効でないって書いてたのに!?」と思うかもしれませんが、アプリケーションが必要とするメモリ量の見積りが単純に低すぎる場合があります。

dirtyなページがあまり多いわけでもなく、swapさせてもswap inが発生しつづける場合は、アプリケーションが必要としている量のメモリを用意できていない場合が考えられます。可能であればアプリケーションの処理を勘案して必要サイズを推定しましょう。
メモリ使用量を調査するためのツールについては以下ドキュメントに紹介されています。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Performance_Tuning_Guide/sect-Red_Hat_Enterprise_Linux-Performance_Tuning_Guide-Memory-Monitoring_and_diagnosing_performance_problems.html

メモリを大量消費するアプリケーションには、自身がどれくらいのメモリを消費するか調整するための設定が用意されていることが多いです。これを増減させて消費メモリ量の増減とパフォーマンスへの影響を観察すると落としどころがみつかるかもしれません。

HPCなどでは「でかいデータを扱うからでかいメモリが要るんだよ!」 みたいなケースもあります。こういうときは金と物理(メモリ)の力で殴るしかないです。

対処ではない: oom_score_adj 

OOM killerの調整ということで検索すると名前がよくでてくるoom_score_adjですが、特定プロセスをOOM killerの対象外にしたり対象になる確率を上げたり下げたりということができます。
しかしOOM killerが発生した原因であるメモリ逼迫については何も改善しないので、あまり意味はありません。

特定のプロセスが死ななければ他がランダムに殺されても大丈夫でしょうか? 普通はランダムに殺されても問題ないというプロセスはほぼ無いはずです。不要なプロセスが存在するなら、特にscoreとかいじる前に単純に起動しないようにする方がいくらかマシです。

oom_score_adjを活用できるシチュエーションをどうにか考えだしてみると、
1)  大事なプロセスに関連したものについてはOOM killerの対象外にしておく
2)  メモリ逼迫を検出するためのカナリア役として優先的に殺されるプロセス(無意味にメモリを確保する)を仕込んでこれを監視に利用
3) カナリア役が殺され次第できるだけ正常な終了手順で大事なプロセスを終了

というような仕組みを作り込む時くらいではないかと思います。OOM killerが動きだしたあとに大容量メモリを確保しているであろう大事なプロセスの終了処理がうまく動くかは非常に疑問ですが、終了処理が追加メモリをあまり使わず動作する場合には使えそうです。

まれに有効: vm.overcommit_memory設定によるovercommitの禁止

vm.overcommit_memoryによるovercommitの禁止は「ほとんど全てのアプリケーションが必要より多くメモリをリクエストしてくる」ため、非常に効率は悪くなりますがメモリ確保の要求量(割り当て量ではない)が特定の値を越えるとOOM Killerによるプロセスの終了が発生します。早く失敗することで延々処理したあと死ぬよりはマシなのではないか、という考え方ですね。

特定のアプリケーションがメモリのほとんどを占め、必要分ギリギリしか要求しないことが分かっている、なおかつ他のプロセスはほぼ動作していない。という前提が満たせる場合はいいのですが、なかなか厳しいです。実際のところアプリケーションによっては通常の利用時に消費されるメモリ量の数十倍から100倍くらいのメモリを要求することもごく普通にあります。極めて特殊な、アプリケーションを選ぶ対策と言ってよいかと思います。

おまけ

いろいろいじりたい人にはRHELのドキュメント「パフォーマンスチューニングガイド」がまずはおすすめ。
https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Linux/7/html/Performance_Tuning_Guide/chap-Red_Hat_Enterprise_Linux-Performance_Tuning_Guide-Performance_Features_in_RednbspHat_EnterprisenbspLinuxnbsp7.html


2016年2月2日

logitechのマウスから謎のイベントが発生してハマった話と暫定的な対策

今にいたるも正確な原因はつかめていないのだが、同じようなハマり方をする人がいるかもしれないのでメモ。

問題


1. 自宅および会社のX環境で、logitech(logicool)の M705, Performance MXを楽しく使っていたのだが、不定期にフォーカスの動き方がおかしくなる現象に見舞われた。

2. 現象が発生するとキーボードのフォーカスはAlt+tabなどで他のwindowに移動できるが、マウスのフォーカスはある1つのwindowに固定されたまま他のwindowに移動できない。

3. マウスの電源オフするかunifiedレシーバを抜くと現象がおさまって普通に動作する。

4. はじめはランダムに発生していたが電源いれるといきなり発生するようになった。

さて、現在のところ原因追及はできていない。だがXよりも下位の、usbhid以下のどこかがおかしいようだ。原因追及は4のほぼ常時発生するようになってから進捗した。

再現&観測手順


1. 他のマウスとM705を2本接続し、M705の電源を一旦切った状態で操作をはじめる

2. xev起動( xev | grep -A3 Button )

3. もうひとつのマウスで xevのwindow上にカーソルを持っていったのちM705の電源を入れると以下のようなイベント表示があらわれる。

ButtonPress event, serial 33, synthetic NO, window 0x3a00001,
    root 0x2da, subw 0x0, time 520218479, (136,90), root:(1337,150),
    state 0x0, button 20, same_screen YES
存在しない20番目のボタンが押されたというイベントだ。そのためこのウィンドウ内でマウスのドラッグがはじまった状態になり、マウスのフォーカスが失われない。(観察した範囲では20番ボタンに限らず2番ボタンなどのイベントも発生する)

なおなんらかの方法(電源offやunified receiverの接続断)で接続を切ると対応するButtonRelease eventが発生してドラッグ状態が解除される。おそらくこれはxinputのどこかの終了処理が行っているのだろう。発生したイベントに対応するボタンがあればそれをクリックすることでもButtonReleaseイベントが発生するので対策になる。

現象が再現している状態でxinputコマンドによりマウスの状態を見ると20番ボタンがdownになっていることでも確認できる。
$ xinput query-state 11
2 classes :
ButtonClass
    button[1]=up
    button[2]=up
    button[3]=up
    button[4]=up
    button[5]=up
    button[6]=up
    button[7]=up
    button[8]=up
    button[9]=up
    button[10]=up
    button[11]=up
    button[12]=up
    button[13]=up
    button[14]=up
    button[15]=up
    button[16]=up
    button[17]=up
    button[18]=up
    button[19]=up
    button[20]=down
    button[21]=up
    button[22]=up
    button[23]=up
    button[24]=up
ValuatorClass Mode=Relative Proximity=In
    valuator[0]=1622
    valuator[1]=1635
    valuator[2]=0
    valuator[3]=-3

暫定的な対策

solaarにより Side Scrolling を有効にすることでこの現象が発生しなくなった。
正直なところ原因をはっきりさせていないので、これで対策になっているのかもわからないのだが、とりあえず動いている。

 暫定的な対策その2(??)

 もろもろ未確認だが、solaarがデバイス検出時行っている初期化処理で何か起きてるような気がする。solaarを全く動作させずに利用していると対策になるかもしれない。

solaarは過去1年くらい平和に動いていたのと、新しく買ったマウスではsolaarで問題が発生しないので、マウスの中の何かしらの状態とsolaarがリバースエンジニアリングして喋ってるlogitech独自プロトコルのあわせ技で問題が発生している可能性が疑われる。。

実験:
  • マウス接続 → solaar起動 → ボタン20down発生 → solaar終了→down継続→マウスoff/on→ボタン20down発生せず
  • solaar起動 → マウス接続 → ボタン20down発生 → マウスoff/on→ボタン20down再度発生

根本的(?)な対策

logicoolのサポートに連絡して押していないボタンについて押したような動作があると相談したところ、故障が疑わしいということで対応していただけました。保証期間内だと相談するといいかもしれません。

 おそらく関連するbugzilla

https://bugs.freedesktop.org/show_bug.cgi?id=77037
closeされちゃってるけどたぶんこの問題と似た話。「xinputで状態確認するとよさそう。俺んとこはドラッグ中のような状態になってた」とコメントを書くためにアカウントを取ろうとしたら失敗したのでメールで申請中……。

2015年12月9日

freeipa+SSSDで2FAを使ってログインする話

この記事はLinux Advent Calendar 2015の9日目の記事です。

イントロ

freeipaでは通常のパスワード認証のほかにOne Time Password(OTP)を利用した2要素認証が簡単に利用できる。googleとかgithubとかでセキュリティ固くしたいときに使うアレだ。
これを利用するときには、従来だと「パスワード文字列とOTP文字列の順にくっつけた文字列」を渡していた。
たとえば以下のような状態だとすると
  • username: kmoriwak
  • password: password
  • OTPのトークンの値: 123123
以下のように入力する。
Login: kmoriwak
Password: password123123
これがRHEL7.2に含まれているpam_sss (sssd-client-1.13.0-40.el7.x86_64) とコンソールのloginやgdmでは以下のようになる。
Login: kmoriwak
First factor: password
Second factor: 123123
何故このように変更されたのか? を見ていこう。

freeipa

この10年ほど、認証周りの標準は Active Directory によって先導されてきた。ADに相当するものとしてはsambaがAD互換の実装を行っているが、UNIX/Linux世界に独特な要件のサポートはいまいちと言わざるをえない状況だった。
UNIX/Linux世界にはLDAP, Kerberos, DNS, PKIなどの基盤技術はあるので、freeipaはこれらを統合したAD相当の認証基盤を作成する。
https://www.freeipa.org/page/About から引用すると:

FreeIPA is an integrated security information management solution combining Linux (Fedora), 389 Directory Server, MIT Kerberos, NTP, DNS, Dogtag (Certificate System). It consists of a web interface and command-line administration tools.
freeipaでは単純なパスワード認証だけではなく、Kerberosを使ったシングルサインオン、OTPを利用した2要素認証をサポートしている。またUNIX/Linux世界向けの機能としては、bindの設定, sshdの公開鍵, sudoポリシーを管理する。

SSSD

SSSDはActive Directoryやfreeipa、LDAPなどによる「ドメイン」と接続し、PAMとNSSのインタフェースに対してドメイン修飾つきの認証を提供する。
たとえばADで AD.EXAMPLE.COM を運用し、freeipaで IPA.EXAMPLE.COM を運用している。さらにLDAP上にも別の名前空間でuid,gidをもっているようなときに、3つ全てのドメインの情報を利用して認証をおこなうことができる。
ドメイン修飾というのは kmoriwak@AD.EXAMPLE.COMと  kmoriwak@IPA.EXAMPLE.COM のようにドメインで重なる(が別のユーザにひもづく)名前がある場合に@以下にドメイン名を記載することでどのドメインかわかるようにするもので、まあだいたいメールのドメイン名みたいなものだ。
PAMとNSSが独立していると複数ドメインを扱うときにうまく連携できないためSSSDが導入され、さらにfreeipaとADとの連携を行う各種の機能や、次節で紹介するキャッシュ機能などが追加されている。

SSSDによるパスワードのキャッシュ

SSSDは認証のキャッシュを行う機能を持つ。いくつか目的があるが、可用性の向上と負荷の軽減が主なものである。
ユースケース1: 普段freeipaで認証するが、何かの問題でfreeipaに接続できない場合もログインサービスを継続したい
ユースケース2: ノートPCを持ち歩く場合に、普段はドメインのDirectory情報をつかった認証をおこなって、オフライン時にはキャッシュを利用して認証したい
ユースケース3: 頻繁に行われる認証をキャッシュしてDirectoryの負荷を下げたい

SSSDはPAM経由で平文のパスワードを受けつけ、これをSSHA2で加工して保存する。
キャッシュは各ドメインに対応したldbに保存されている。以下のようにtdbtoolで見える。
# tdbtool /var/lib/sss/db/cache_example.com.ldb show "DN=NAME=kmoriwak,CN=USERS,CN=EXAMPLE.COM,CN=SYSDB\00"
(中略)
[620] 39 30 33 5A 00 63 61 63  68 65 64 50 61 73 73 77  903Z.cac hedPassw
[630] 6F 72 64 00 01 00 00 00  6A 00 00 00 24 36 24 6B  ord.... j...$6$k
[640] 76 75 50 49 53 2E 41 4F  59 68 56 56 67 4E 4F 24  vuPIS.AO YhVVgNO$
[650] 33 4A 61 2F 36 63 34 4C  6B 77 7A 32 4B 64 6A 51  3Ja/6c4L kwz2KdjQ
[660] 34 73 63 5A 7A 6D 46 75  30 49 56 7A 4B 71 61 6F  4scZzmFu 0IVzKqao
[670] 4A 37 72 52 2F 54 76 53  6E 50 6E 55 6B 75 54 30  J7rR/TvS nPnUkuT0
[680] 6B 37 32 54 45 67 67 2E  41 5A 50 41 76 66 36 69  k72TEgg. AZPAvf6i
[690] 37 49 4E 54 34 64 38 39  46 4F 59 32 45 51 35 30  7INT4d89 FOY2EQ50
[6A0] 73 7A 30 33 66 30 00 6C  61 73 74 43 61 63 68 65  sz03f0.l astCache

(以下略)
オフライン時にどの程度の時間(24時間や48時間など)キャッシュを維持するかはsssdの設定次第で変更できるが、デフォルトでは制限がおこなわれていないためオフライン時の認証には向いている。

パスワードとOTPトークンを区別したいケース

OTPのトークンは設定によるが30秒や60秒でどんどん変わってしまうのでキャッシュをすることには意味がない。
さらに2要素認証を利用するにあたって、常に2要素を接続した文字列を利用しつづけることは現実的ではない。具体的には固定的なパスワードと連携したキーリングのアンロックやディレクトリの暗号化には共通鍵を使いたいのでOTPをそのまま適用することができない。
またオフライン時に認証を継続したい場合には、OTPのトークンを検証できないためこれを使わないで認証したい。入力された[パス ワードとOTPを組み合わせた文字列]をそのままハッシュ化してキャッシュすると次回オフライン時の認証は必ず失敗することになってしまう。
このためパス ワードとOTPトークンをなんとか区別したい。トークンの長さが常に一定であれば簡単なのだが、残念ながらそうではない。

freeipaへの認証とOTP

freeipaへの認証は基本的にKerberosへのログインである。OTPを利用する場合の動きについては http://www.freeipa.org/page/V4/OTP#Design に詳しいが、OTP over RADIUS を使って認証時にLDAP上のユーザ属性にもとづいてfreeipa組み込みのOTPに対応する他、サードパーティのOTPにも対応できるようになっている。
このため事前にトークンの文字列長を一般に確定させる方法は存在しない(トークン長が常に一定だとも仮定できない)。実装によってはKerberosのチャレンジとしてトークン長が帰ってくる (http://tools.ietf.org/html/rfc6560)ものもあるので、これを利用してSSSDとpam_sssがトークンを除去できることもある。freeipaに内蔵のHOTP,TOTP実装はトークン長を返すのでうまく動作できる。
力技での対応策としては入力文字列を最後から1文字ずつ削除してハッシュを作る作戦があるがあまり望ましくない。たとえば入力が16文字だとして、16,15,14...と文字数を減らしていきそれぞれのハッシュ値を計算してキャッシュに維持する。しかしこれを単純に適用すると短い文字列に対するハッシュを生成し、結果としてオフライン時の認証が脆弱になる危険がある。たとえば16文字の入力から12文字削って先頭4文字に対するハッシュを維持するようなことがあればオフライン認証に対しての総当たり攻撃が非常に簡単になるだろう。

解決策

ここで最初のふるまいにもどる。SSSDとpam_sssが協調し、事前にユーザ情報から認証のタイプを確認して2つの要素を分けて入力させることでパスワードとトークンの分離をするためのヒューリスティックは不要となる。
なおpam_conv()は複数入力をサポートしておりgdmやloginは複数入力に対応するが、歴史的理由により1つの文字列しか認証用に入力できない処理系も多数存在する。そのためpam_sssの実装では、パスワードとトークンが接続された文字列と空文字列、パスワードとトークンを別々に入力の2種類に対応している。
sshdから使われる場合のみワークアラウンドとしてパスワードとトークンが接続された文字列が2回入力されるパターンに対応している。

2015年7月22日

RHELセキュリティ情報の入手と適用

修正が作成されるまで

一般にプログラムに問題があるときは、プログラムのソースコードを修正して対応します。RHELのほどんとのパッケージではアップストリームのソースコードを直接変更せずそのままにしておき、修正は複数の「パッチ(修正用の差分情報)」を追加することでおこなっています。
RHELで利用されているrpm(RPM Package Manager)では、元のソースコードとパッチ、さらにそれらを処理して実際に実行されるプログラムに変換するための情報を「ソースrpm」とよばれるパッケージにまとめます。
ソースrpmから「バイナリrpm」を作成します。これは実際に実行できるプログラムなどが入ったパッケージで、通常「rpmパッケージ」という場合にはこのバイナリrpmのことをさしています。
通常1つのソースrpmから、複数のバイナリrpmが作成されます。動作するCPUアーキテクチャや利用するライブラリ、オプションなどによって、同じソースrpmから異なるバイナリrpmが作成されます。

errataとアドバイザリ

このように作成されたソースrpmおよびバイナリrpmを公開するデータベースがerrataです。
errataに含まれている、修正を公開する単位が「アドバイザリ」です。アドバイザリは、1つまたは複数のソースrpmの修正に対応したアナウンスで、以下のような情報を含みます。
  • アドバイザリのID (RH?A-XXXX:YYYY というフォーマットです。 ?はS,B,Eのいずれか、XXXX,YYYYは数字です)
  • 修正された問題の要約
  • 発行日、最終更新日
  • (セキュリティ修正のみ)重要度
  • (セキュリティ修正のみ)関連する脆弱性情報
  • 影響をうける製品
  • 更新されたパッケージとそのバージョン
  • 関連するbugzillaエントリ(通常は複数の修正が含まれています)

errataはそれぞれのアドバイザリがレコードとしてならんだデータベースになっています。
重要な特徴として、errataではそれぞれのアドバイザリに順序関係があり、同じ製品であれば新しいアドバイザリには、それ以前のアドバイザリで提供された修正が全て反映されています。
たとえばfirefoxがアドバイザリ A, B, Cの順に3回修正されたとすると、Aに含まれている修正は全てB,C にも含まれ、Bに含まれている修正はCにも含まれています。
つまり、常に最新のパッケージを適用することで、全ての既知の問題に対応できます。「Bで修正された部分は利用するがAとCの問題はそのまま放置する」というような選択肢をなくすことで、管理がシンプルになっています。

アドバイザリの種類は?

アドバイザリには3種類の分類があり、それぞれIDが以下の4文字ではじまります。
  • RHSA セキュリティ上の脆弱性を修正したアドバイザリ
  • RHBA バグを修正したアドバイザリ
  • RHEA 機能拡張したアドバイザリ
RHSAおよびRHBAでは、基本的に対応する問題を修正するだけで、新しい機能が追加されることはありません。機能拡張は基本的にRHEAだけで行われます。

errata情報の入手

errataの情報は公開されており、以下のwebページから参照することができます。


また、通知を受ける方法としてRHELでは3通りの方法を提供しています。
1. yum-updatesd によるメール送信、log出力
RHELに同梱されている yum-updatesd は定期的にカスタマーポータルに接続して更新情報を取得し、更新パッケージがあれば通知します。
通知方法にはe-mail, log出力, dbusが選択でき、デフォルト設定ではdbusによる通知を利用して、GUI環境でアップデートの通知を画面表示させます。この設定を変更することでメールやログの形でアップデートの通知を受けとることができます。
詳しくは、「man yum-updatesd.conf」としてマニュアルを参照してください。
2. Red Hat カスタマーポータルからのメールによる通知
カスタマーポータルのErrata notifications設定により、errata出荷時にメールで通知を受けとることができます。
3. メーリングリスト、RSSフィードによる通知
errataの更新についての通知を、RSSフィードおよびメーリングリストでおこなっています。

脆弱性のCVE numberから対応するerrataアドバイザリを確認

RHSA には、Common Vulnerabilities and Exposures (CVE)の関連する脆弱性情報へのリンクが含まれています。以下のサイトで、CVE numberからerrataのアドバイザリを逆引きすることができます。ニュースサイト等で見た脆弱性が製品への影響しているかの確認や、既に出荷され ていればerrataのIDを確認することができます。


yumコマンドによるセキュリティ修正の確認・対応

インターネット接続が可能であるか、Red Hat Satelliteが利用可能な環境では、yumコマンドによりセキュリティ対応をおこなうことが可能です。
サマリの表示
# yum updateinfo
Loaded plugins: langpacks, product-id, subscription-manager
Updates Information Summary: available
    3 Security notice(s)
        2 Critical Security notice(s)
        1 Important Security notice(s)
    2 Bugfix notice(s)
    2 Enhancement notice(s)
updateinfo summary done
適用可能なerrataの一覧
# yum updateinfo list available
Loaded plugins: langpacks, product-id, subscription-manager
RHSA-2015:1443 Important/Sec. bind-32:9.9.4-18.el7_1.2.x86_64
RHSA-2015:1443 Important/Sec. bind-libs-32:9.9.4-18.el7_1.2.x86_64
RHSA-2015:1443 Important/Sec. bind-libs-lite-32:9.9.4-18.el7_1.2.x86_64
RHSA-2015:1443 Important/Sec. bind-license-32:9.9.4-18.el7_1.2.noarch
RHSA-2015:1443 Important/Sec. bind-utils-32:9.9.4-18.el7_1.2.x86_64
RHSA-2015:1207 Critical/Sec.  firefox-38.1.0-1.el7_1.x86_64
RHSA-2015:1229 Critical/Sec.  java-1.7.0-openjdk-1:1.7.0.85-2.6.1.2.el7_1.x86_64
RHSA-2015:1229 Critical/Sec.  java-1.7.0-openjdk-devel-1:1.7.0.85-2.6.1.2.el7_1.x86_64
RHSA-2015:1229 Critical/Sec.  java-1.7.0-openjdk-headless-1:1.7.0.85-2.6.1.2.el7_1.x86_64
RHBA-2015:1192 bugfix         openssl-1:1.0.1e-42.el7_1.9.x86_64
RHBA-2015:1192 bugfix         openssl-libs-1:1.0.1e-42.el7_1.9.x86_64
RHBA-2015:1474 bugfix         python-chardet-2.2.1-1.el7_1.noarch
RHEA-2015:1473 enhancement    python-urllib3-1.10.2-1.el7_1.noarch
RHEA-2015:1476 enhancement    redhat-access-insights-1.0.4-0.el7_1.noarch
updateinfo list done
アドバイザリ詳細の確認
# yum info-sec --advisory RHSA-2015:1443
Loaded plugins: langpacks, product-id, subscription-manager

===============================================================================
  Important: bind security update
===============================================================================
  Update ID : RHSA-2015:1443
    Release :
       Type : security
     Status : final
     Issued : 2015-07-20 00:00:00
       Bugs : 1237258 - CVE-2015-4620 bind: abort DoS caused by uninitialized value use in isselfsigned()
       CVEs : CVE-2015-4620
Description : The Berkeley Internet Name Domain (BIND) is an implementation of
            : the Domain Name System (DNS) protocols. BIND
            : includes a DNS server (named); a resolver library
            : (routines for applications to use when interfacing
            : with DNS); and tools for verifying that the DNS
            : server is operating correctly.
            :
            : A flaw was found in the way BIND performed DNSSEC
            : validation. An attacker able to make BIND
            : (functioning as a DNS resolver with DNSSEC
            : validation enabled) resolve a name in an
            : attacker-controlled domain could cause named to
            : exit unexpectedly with an assertion failure.
            : (CVE-2015-4620)
            :
            : Red Hat would like to thank ISC for reporting this
            : issue.
            :
            : All bind users are advised to upgrade to these
            : updated packages, which contain a backported patch
            : to correct this issue. After installing the
            : update, the BIND daemon (named) will be restarted
            : automatically.
   Severity : Important
updateinfo info done
CVE IDを指定してのアップデート
# yum update --cve CVE-2015-4620
アドバイザリIDを指定してのアップデート
# yum update --advisory RHSA-2015:1443

RHELのバージョンによりyumコマンドで利用するオプションが変わります。以下のナレッジベースでRHELの5,6,7それぞれでの操作をご案内しています。

参考情報

セキュリティガイド 第3章 システムを最新の状態に保つ