4256
セキュリティと監査証跡の目的で自動的に操作ログを残すにはどうしたらよいでしょうか。  
ユーザが気が付かないうちにひっそりとログを取るのが理想です。  
ログに残したい情報は次の通りです。

  * ユーザ名
  * ユーザが入力したコマンドライン
  * 端末への出力
  * タイムスタンプ
CC BY-SA 3.0
2015-01-06T16:52:36.680
3216
2016-10-14T12:17:25.027
5008
question
linux security
ターミナルの操作ログを自動で残したい
プロセスアカウンティング用に広く利用できる物として "[Process Accounting
Utility](http://www.gnu.org/software/acct/)" があります。環境によって、パッケージの名前が、 `psacct`
もしくは `acct` になっているものです。

```

    $ # インストール (ubuntu)
    $ apt-get install acct
    
```

## 用意されているコマンド

  * `lastcomm`: 実行されたコマンドの表示
  * `ac` : ユーザの接続時間の表示
  * `sa`: 過去に実行されたコマンドの集計／フィルタ

※ 質問の要件を満たすために、一般ユーザーからはこれらのコマンドが実行できないようにしてください。

## アカウンティングサービスの実行

```

    $ # サービスの開始
    $ /etc/init.d/acct start
    
    $ # サービスの停止
    $ /etc/init.d/acct stop
    
```

実際は、`accton` コマンドによってプロセス監視が始められます。収集情報の格納先は、デフォルトで `/var/log/account/pacct`
または `/var/account/pacct` になります。

## 過去に実行されたコマンド情報を知る

`lastcomm` コマンドを利用します。

```

    $ lastcomm higon
    
    byobu-status      F    higon    __         0.00 secs Wed Jan  7 03:23
    sed                    higon    __         0.00 secs Wed Jan  7 03:23
    sudo             S     root     pts/18     0.03 secs Wed Jan  7 03:21
    apt-get          S     root     pts/18     1.85 secs Wed Jan  7 03:23
    dpkg                   root     pts/18     0.00 secs Wed Jan  7 03:23
    dpkg                   root     pts/18     0.00 secs Wed Jan  7 03:23
    dpkg                   root     pts/18     0.00 secs Wed Jan  7 03:23
    apt-get           F    root     pts/18     0.00 secs Wed Jan  7 03:23
    sh                     root     pts/18     0.00 secs Wed Jan  7 03:23
    touch                  root     pts/18     0.00 secs Wed Jan  7 03:23
    ...
    
```

※ 終了したコマンドのみ記録されています。実行中のコマンドは取れません。

以下のように、絞り込みやグループ化のオプションが用意されています。

特定の接続の履歴を知る:

```

    $ lastcomm --tty pts/18
    
```

特定のコマンドの実行を知る:

```

    $ lastcomm --strict-match --command vi
    
```

## ユーザの接続時間の表示

ユーザ毎日付毎の接続時間:

```

    $ ac -d -p
    
```

## サマライズ

ユーザ毎に履歴を表示:

```

    $ sa --print-users
    
```

ユーザ毎にサマライズを表示:

```

    $ sa --user-summary
    
```

出力例:

```

       9000    3794.72re       0.09cp         0avio      2914k
          23      94.46re       0.05cp         0avio      3516k   ***other*
          11       0.35re       0.01cp         0avio      2890k   lastcomm
           2       0.01re       0.01cp         0avio     18432k   command-not-fou
           2    3699.26re       0.00cp         0avio     54160k   apache2*
           2       0.00re       0.00cp         0avio      8518k   lsb_release
           4       0.01re       0.00cp         0avio      5465k   dpkg
           2       0.01re       0.00cp         0avio      4092k   grep
           3       0.00re       0.00cp         0avio     33328k   php5
        1108       0.34re       0.00cp         0avio      1111k   sh
        1104       0.00re       0.00cp         0avio      3950k   tmux
        1104       0.00re       0.00cp         0avio      4618k   sed
           2       0.00re       0.00cp         0avio      4282k   sadc
    
```

各カラムの説明は `man sa` に任せます。

## `logrotate` の設定

環境によってはパッケージ導入時に logrotate.d 以下に設定ファイルがおかれます。 Ubuntu
だと以下のようなファイルで良いでしょう。（ログは圧縮されています。 過去ログを `sa -f`
オプションで指定するときには、展開したデータを渡す必要があります。）

```

    /var/log/account/pacct {
        compress
        delaycompress
        notifempty
        daily
        rotate 31
    
        rotate 31
        create 0600 root root
        missingok
        postrotate
            /etc/init.d/acct restart
        endscript
    }
    
```

以上です。使用にあたっては、監視の粒度に注意したいところです。`acct`
は、大量にコマンド実行履歴を集めますが、コマンドの引数は見ません。ディレクトリ監視、ネットワーク監視、負荷監視などとあわせることで、より細かなことがわかるようになるでしょう。

最初から細かな記録を取りたい場合は、プロセス監視デーモンとして、 [auditd](http://linux.die.net/man/8/auditd)
があります。こちらは Linux
カーネルのシステムコールレベルで環境を記録するもので、事細かなシステムのログを取ります。コマンドの引数なども記録するのですが、漏れのない徹底的な監視環境を目指していない限り、
`acct` の利用が適切かと思います。

参考:

  * GNU, [Process Accounting Utility](http://www.gnu.org/software/acct/)
  * ITMedia, [アカウントの利用統計が知りたい](http://www.itmedia.co.jp/help/tips/linux/l0677.html)
  * [How to keep a detailed audit trail of what’s being done on your Linux systems](http://www.cyberciti.biz/tips/howto-log-user-activity-using-process-accounting.html)
CC BY-SA 3.0
2015-01-06T19:11:39.773
3217
2015-01-07T14:56:59.063
2015-01-07T14:56:59.063
4978
4978
3216
answer
Linux OS には LD_PRELOAD という環境変数が用意されています。この変数に shared object
を指定すると、コマンド(dynamic linkされたもののみ)の実行開始時に dynamic link されます。そして、その shared object
内にある function を呼び出して実行することが可能になります。  
この機能を利用して、例えば libc.so 内にある system call `execve(2)` を intecept する事が可能になります。  
この `execve(2)` は bash などのシェルプログラムなどがユーザから入力されたコマンドを実行する際に使われます。そのため、LD_PRELOAD
を使って「ユーザが入力したコマンドライン」を記録することが可能になります。  
以下は至極単純なサンプルコードです(配列のサイズなどはいい加減です)。ユーザ名などは stderr へ出力していますが、`syslog(3)`
などを使用して syslog へ出力した方が良いでしょう。

`hook_execve.c`

```

    #include <dlfcn.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <pwd.h>
    #include <time.h>
    
    static int (*_execve)(const char *filename, char *const argv[], char *const envp[]);
    
    int execve(const char *filename, char *const argv[], char *const envp[]) {
      // Save original execve() function
      _execve = (int(*)(const char *, char *[], char *[]))dlsym(RTLD_NEXT, "execve");
    
      // Get user name
      struct passwd *pw = getpwuid(getuid());
      char *username = pw ? pw->pw_name : "";
    
      // Construct command line string
      int i = 0; int len = 0; char cmdline[256];
      while(argv[i]){
        strncpy(cmdline + len, argv[i], strlen(argv[i]));
        len += strlen(argv[i++]);
        strcpy(cmdline + (len++), " ");
      }
    
      // Time stamp
      char t_string[21];
      time_t now = time(NULL);
      strftime(t_string, 20, "%F %T", localtime(&now));
    
      // Logging
      fprintf(stderr, "%s: username = %s, command line = %s\n", t_string, username, cmdline);
    
      // Call original execve()
      return (*_execve)(filename, argv, envp);
    }
    
```

コンパイルを行って shared object を作成します。

```

    $ gcc -D_GNU_SOURCE -shared hook_execve.c -o hook_execve.so -ldl
    
```

LD_PRELOAD 環境変数に hook_execve.so を指定して bash を実行します。

```

    $ LD_PRELOAD=./hook_execve.so bash -l
    $ ldd /bin/bash
    linux-gate.so.1 =>  (0xb7777000)
    ./hook_execve.so (0xb7772000)
    
    $ ls
    2015-01-07 04:37:38: username = nemo, command line = ls -FC --show-control-chars 
    hook_execve.c  hook_execve.so*
    
    $ wget http://www.faridani.me/data/Galton.csv
    2015-01-07 04:39:02: username = nemo, command line = wget http://www.faridani.me/data/Galton.csv 
    --2015-01-07 04:39:02--  http://www.faridani.me/data/Galton.csv
    
```

期待通りに動作していますね。後は、例えば Ubuntu Linux であれば `/etc/environment` に LD_PRELOAD
の設定を追加しておくことになるでしょう。  
しかし、この方法には多くの問題点があります。

  * ユーザが LD_PRELOAD 環境変数をリセットした場合には動作しない
  * ユーザが static link されたシェルプログラムを使っている場合には動作しない
  * 一般ユーザだけでなく、root を含む全ユーザで実行されるので記録が膨大な量になる可能性が高い
  * `execve(2)` が call される度に記録処理が実行されるのでシステムの負荷が高くなりやすい

したがって、実運用のシステムで使うのはほぼ無理、でしょうね。まぁ、参考程度に見ておいて下さい。
CC BY-SA 3.0
2015-01-06T20:04:03.993
3218
2015-01-06T20:40:03.927
2015-01-06T20:40:03.927
3216
answer
Bash限定になりますが、端末への出力以外は全て実現可能です。

ref: <http://tiswww.case.edu/php/chet/bash/NEWS>

_l. There is a new configuration option (in config-top.h) that forces bash to
forward all history entries to syslog._

  * UID(ユーザ名) 
  * ユーザが入力したコマンド
  * タイムスタンプ

詳細はこちらに記載がありますので参照下さい。

<http://d.hatena.ne.jp/tatac1/20120724/1343106093>

ログを残す際には、ホストの時刻同期、操作される端末と別のホストにSyslogを転送する、更にLDAP等でユーザーを管理すると更にベターです。

追記：
ひっそりとログを取る事を考えるのであれば、syslogデータの表示(通常はrootのみ読み取り可能)、bashのバイナリ解析、ptrace等で確認しない限り気付かれにくいと思います。
CC BY-SA 3.0
2015-01-07T01:20:00.063
3224
2015-01-07T05:04:57.593
2015-01-07T05:04:57.593
570
570
3216
answer
ログインシェルをttyrecなどでラップしたシェルにすれば取れます（取っていました）。
CC BY-SA 3.0
2015-01-08T01:02:21.553
4256
2015-01-08T01:02:21.553
321
3216
answer
scriptというのもあります。  
<http://dev.classmethod.jp/operation/logging_operation_using_script_and_psacct/>

要件を満たせるか確認してないですが、割りとサクッと使えるので検討してみるのも良いかと。
CC BY-SA 3.0
2016-10-14T12:17:25.027
29559
2016-10-14T12:17:25.027
3216
answer