Macのログインを検出し、Slackへ通知する

<img width="595" alt="スクリーンショット 2016-10-23 12.51.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/503b2af7-9ce4-d3f6-09f6-a654a8c9defb.png">
※ slackのformatをいじったらこんな感じになりました。

# 概要
* サービス管理フレームワーク(`launchd`)を利用し、スクリプトを定期実行する
* スクリプトで、ログイン成功を検出し、Slackへ通知する
* ライフログとして利用したり、コンピュータ紛失時のアクティビティ監視に応用できる

# 環境
以下の環境で、動作確認を行いました。

```
$ sw_vers                                                                          
ProductName:	Mac OS X
ProductVersion:	10.11.5
BuildVersion:	15F34
```

# ログインを検出する
LINEやAmazon、Googleなどは、ログイン時にメールやメッセージを、ユーザに送るようになりました。不正ログインによる被害を最小限にする施策です。このようなログインアクティビティの通知は、不正ログインの被害を抑えるだけでなく、ライフログとして活用できます。

今回は、日常的に利用している**Mac OS X**と**Slack**を使ってログインアクティビティの通知を行います。

# Mac OS Xのログイン履歴
Macの場合、ログイン成功のログは、`/var/log/accountpolicy.log`にあります[^1]。ほかのさまざまなログは、`console.app`から確認できます。

[^1]: バージョンごとに違う可能性があるので注意して下さい。

ログイン成功のログは以下のようになっています。各レコードで`completed`という文字列を見つければよさそうです。

```
Oct 23 03:36:55 (73.392.1) AuthenticationAllowed completed: record "tamanobi", result: Success (0).
Oct 23 03:36:55 (73.393.1) AuthenticationAllowed completed: record "tamanobi", result: Success (0).
Oct 23 03:40:41 (73.394.1) AuthenticationAllowed completed: record "tamanobi", result: Success (0).
Oct 23 03:40:41 (73.395.1) AuthenticationAllowed completed: record "tamanobi", result: Success (0).
```

# 10秒以内にログインしていたら通知する
上記で説明したログには、日時が記録されています。チェックしたときの日時と、何秒前の記録まで通知するか決めればよさそうです。

シェルスクリプトを書いてみました。最後に出てくる`webhook.sh`は、[[10分で出来る]シェルスクリプトの結果をslackに投稿](http://qiita.com/tt2004d/items/50d79d1569c0ace118d6)を参考にしてください。


```noticeLoginActivity.sh
#!/bin/bash
set -eux

# 実行時の日時から、何秒前のレコードをSlack通知の対象とするか
INTERVAL=10
# ログファイルのフルパス
LOGFILENAME=$1

function dateComp()
{
  # Oct 23 01:59:20 形式から経過秒に変換
  ARG1_SECOND=`date -j -f "%b %d %H:%M:%S" "$1" "+%s"`
  ARG2_SECOND=`date -j -f "%b %d %H:%M:%S" "$2" "+%s"`

  expr $ARG1_SECOND - $ARG2_SECOND
}

# ログのレコードから、時間を"%b %d %H:%M:%S"形式で取得
function record2date()
{
  RECORD=`echo $1 | awk '{print $1 " " $2 " " $3}'`
  echo $RECORD
}

NOW=`date '+%b %d %H:%M:%S'`
PREV=`date -v-${INTERVAL}S '+%b %d %H:%M:%S'`

# ダブルクオーテーションがあると変数展開時に厄介なので、アスタリスクに置換
LAST_RECORD=`tail -n 1 "$LOGFILENAME" | sed -e 's/"/*/g'`

DATETIME_LAST_RECORD=`record2date "$LAST_RECORD"`
MAX_DIFF_DATETIME=`dateComp "$NOW" "$PREV"`
DIFF_DATETIME_LAST_RECORD=`dateComp "$NOW" "$DATETIME_LAST_RECORD"`

if [[ $MAX_DIFF_DATETIME -gt $DIFF_DATETIME_LAST_RECORD ]]; then
  if [[ "$LAST_RECORD" =~ completed ]];then
    echo "User logged in!\n$LAST_RECORD" | sh /Users/tamanobi/webhook.sh
  fi
fi
```

# 定期的にログを確認して、監視をする
監視にはログインフックを利用することもできますが、定期的にログを確認するシンプルなアプローチを今回は採用します。

## `launchd`を利用してプログラムを定期実行する
定期実行といえば、cronですが今回は利用しません。代わりに、`launchd`というサービス管理フレームワークを利用します。

以下は、`launchd`でできることの一例です。

* `cron`のように任意のスクリプトを定期実行する
* ファイルの変更を監視し、変更時に任意のスクリプトを実行する

## プログラムを定期実行するための設定ファイルを作成する
`launchd`を利用するには、XMLで書かれた設定ファイルを登録する必要があります。今回用意した設定ファイルを載せます。

記述量は多めですが、重要なのは以下の３つだけです。
* プロセスのラベル
* コマンド
* 定期実行の間隔（秒)

```~/Library/LaunchAgents/monitoringLoginActivity.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>Label</key>
     <!-- プロセスのラベル -->
     <string>monitoringLoginActivity</string>

     <key>ProgramArguments</key>
     <array>
          <!-- コマンド -->
          <string>bash</string>
          <!-- コマンドに渡す第一引数 -->
          <string>/Users/tamanobi/noticeLoginActivity.sh</string>
          <!-- コマンドに渡す第二引数 -->
          <string>/var/log/accountpolicy.log</string>
     </array>

     <key>StartInterval</key>
     <!-- 定期実行の間隔（秒） -->
     <integer>10</integer>

     <key>StandardOutPath</key>
     <!-- 出力を保存するファイルへのパス -->
     <string>/tmp/monitoringLoginActivity.out</string>

     <key>StandardErrorPath</key>
     <!-- エラーを保存するファイルへのパス -->
     <string>/tmp/monitoringLoginActivity.err</string>
</dict>
</plist>
```

# 動かしてみる
登録する前に必要なファイルの確認です。以下のファイルが必要です。

* ~/Library/LaunchAgents/monitoringLoginActivity.plist
* /Users/tamanobi/noticeLoginActivity.sh
* /Users/tamanobi/webhook.sh[^2]

[^2]: [[10分で出来る]シェルスクリプトの結果をslackに投稿](http://qiita.com/tt2004d/items/50d79d1569c0ace118d6)を参考にしてください。

## `launchd`に登録する
`launchd`を操作するには、`launchctl`コマンドを使用します。`launchctl load`でplistを登録できます。登録したら、すぐに10秒ごとに監視が動き出します。

```
$ launchctl load ~/Library/LaunchAgents/monitoringLoginActivity.plist
```

エラーログや出力結果は、設定したとおり`/tmp/monitoringLoginActivity.err`と`/tmp/monitoringLoginActivity.out`で確認できます。

### `launchd`の登録を削除するには `launchctl unload`を使う
登録したものは`launchctl unload`で削除できます。

```
$ launchctl unload ~/Library/LaunchAgents/monitoringLoginActivity.plist
```

### `launchd`の登録一覧をみるには `launchctl list`を使う
登録したものは`launchctl list`で確認できます。量が多いので、設定したラベルでgrepすることをおすすめします。

```
$ launchctl list
```

# 完成！（実際にログインし直してみる）
実際にログインし直してみると、Slackに通知が来ます。

<img width="599" alt="スクリーンショット 2016-10-23 6.43.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/38967/c09ccf65-9f53-62f6-52fc-ff2991cec234.png">

# ログインアクティビティを通知する

# まとめ
この記事では、MacのログインアクティビティをSlackに通知する方法を紹介しました。

一例として、Slackに通知を送りましたが、スクリプト次第で別のサービスへ通知を送ることもできます。ログインしたタイミングで、特定のビルドを走らせたり、本日の予定を画面に表示させることもできるでしょう。

また、ログインアクティビティをストレージに保存しておけば、ライフログとして活用できるでしょう。職業プログラマなら生産性の指標になるかもしれません。

# 参考リンク
* [launchdに関するリソース制限の設定まとめ](http://qiita.com/rsahara/items/7d37a4cb6c73329d4683)
* [[10分で出来る]シェルスクリプトの結果をslackに投稿](http://qiita.com/tt2004d/items/50d79d1569c0ace118d6)
* [launchdで定期的にスクリプトを実行](qiita.com/rsahara/items/7d37a4cb6c73329d4683)
* [cronからlaunchdへ（より効率的なジョブ管理を目指して)](http://d.hatena.ne.jp/zariganitosh/20090308/1236539611)
* [Mac（OS X）ではcronじゃなくてlaunchdでやる](http://furudate.hatenablog.com/entry/2014/11/09/155017)
* [LaunchAgent に登録して、スクリプトを自動起動する](http://takuya-1st.hatenablog.jp/entry/2015/06/02/145548)
* [launchd を使ってスクリプトを自動実行してみた](https://macperson.net/mac-launchd/)