bashのヒストリをログ出力するようにjenkinsでビルドする


bash_historyを時刻を出してログに出したいということで、
セキュリティというか監査的に需要がありまして対応いたしました。
なんでjenkinsかというと可視化と自動化的な需要があったためです。
（脆弱性の対応で何回もビルドすることになりましたがjenkinsジョブになってるので埋もれた情報もなくて便利でした）

jenkinsさんのジョブは以下のような感じです。
単にパラメータつきのシェルの実行です。

```
#!/bin/bash
topdir="${HOME}/rpmbuild"
rpmdir="${topdir}/RPMS/x86_64"

if [ -f ${HOME}/.rpmmacros ];then
  echo "%_topdir ${topdir}" > "${HOME}/.rpmmacros"
  echo "%_signature gpg" >> "${HOME}/.rpmmacros"
  echo "%_gpg_name D279****" >> "${HOME}/.rpmmacros"
fi
if [ -d ${HOME}/rpmbuild ];then
  mv ${HOME}/rpmbuild{,.`date +%Y%m%d.%H%M`}
  mkdir -p ${HOME}/rpmbuild/SRPM
fi

case "${TARGET}" in
 *.src.rpm) rpm -Uvh "${TARGET}"
  cp -p ${topdir}/SPECS/bash.spec{,.org}
  sed -i 's/make "CFLAGS=$CFLAGS -fwrapv" "CPPFLAGS=-D_GNU_SOURCE -DRECYCLES_PIDS `getconf LFS_CFLAGS`"/make "CFLAGS=$CFLAGS -fwrapv" "CPPFLAGS=-D_GNU_SOURCE -DRECYCLES_PIDS `getconf LFS_CFLAGS` -DSYSLOG_HISTORY"/g' ${topdir}/SPECS/bash.spec
  sed -i 's/Release: 15%{?dist}.2/Release: 15%{?dist}_5.2_hoge_4/g' ${topdir}/SPECS/bash.spec
  sed -i '78s/^$/\n/g' ${topdir}/SPECS/bash.spec
  sed -i '79s/^$/Patch134: bash-syslog_facirity.patch\n/g' ${topdir}/SPECS/bash.spec
  sed -i '149s/^$/%patch134 -p1\n/g' ${topdir}/SPECS/bash.spec
  cd ${HOME}/rpmbuild/SOURCES/
  tar xzf bash-4.1.tar.gz
  cp -rp bash-4.1{,.org}
  sed -i 's/#  define SYSLOG_FACILITY LOG_USER/#  define SYSLOG_FACILITY LOG_LOCAL6/g' ${HOME}/rpmbuild/SOURCES/bash-4.1/config-top.h
  sed -i 's!(SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line)!(SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d PPID=%d SID=%d User=%s UID=%d CMD=%s", getpid(), getppid(), getsid(getpid()), current_user.user_name, current_user.uid, line)!g' ${HOME}/rpmbuild/SOURCES/bash-4.1/bashhist.c
  sed -i 's!(SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc)!(SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d PPID=%d SID=%d User=%s UID=%d CMD=%s", getpid(), getppid(), getsid(getpid()), current_user.user_name, current_user.uid, trunc)!g' ${HOME}/rpmbuild/SOURCES/bash-4.1/bashhist.c
  diff -crN bash-4.1.org bash-4.1 > ${HOME}/rpmbuild/SOURCES/bash-syslog_facirity.patch
  rpmbuild -ba ${topdir}/SPECS/bash.spec
 ;;
 *)  echo 'environment variable TARGET must be set.'; exit 1;;
esac
```

TARGETにしたパラメータは以下です。
```http://vault.centos.org/6.5/updates/Source/SPackages/bash-4.1.2-15.el6_5.2.src.rpm```

`${HOME}`は`/var/lib/jenkins`です。

ソースRPM落として入れて
ソースアーカイブ解凍して
sedしてパッチ作って
作ったパッチあてられるようにspecファイルsedして
ビルドする
という流れになっております。
diffでディレクトリまるごとパッチ作るオプションは-crNだったことを調べて知りました。
パッチは敢えて載せると以下のような感じです。

```
# cat bash-syslog_facirity.patch
diff -crN bash-4.1.org/bashhist.c bash-4.1/bashhist.c
*** bash-4.1.org/bashhist.c     2009-08-15 04:33:02.000000000 +0900
--- bash-4.1/bashhist.c 2014-09-29 10:19:48.611631770 +0900
***************
*** 705,716 ****
    char trunc[SYSLOG_MAXLEN];

    if (strlen(line) < SYSLOG_MAXLEN)
!     syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d UID=%d %s", getpid(), current_user.uid, line);
    else
      {
        strncpy (trunc, line, SYSLOG_MAXLEN);
        trunc[SYSLOG_MAXLEN - 1] = '\0';
!       syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d UID=%d %s", getpid(), current_user.uid, trunc);
      }
  }
  #endif
--- 705,716 ----
    char trunc[SYSLOG_MAXLEN];

    if (strlen(line) < SYSLOG_MAXLEN)
!     syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY: PID=%d PPID=%d SID=%d User=%s UID=%d CMD=%s", getpid(), getppid(), getsid(getpid()), current_user.user_name, current_user.uid, line);
    else
      {
        strncpy (trunc, line, SYSLOG_MAXLEN);
        trunc[SYSLOG_MAXLEN - 1] = '\0';
!       syslog (SYSLOG_FACILITY|SYSLOG_LEVEL, "HISTORY (TRUNCATED): PID=%d PPID=%d SID=%d User=%s UID=%d CMD=%s", getpid(), getppid(), getsid(getpid()), current_user,user_name, current_user.uid, trunc);
      }
  }
  #endif
diff -crN bash-4.1.org/config-top.h bash-4.1/config-top.h
*** bash-4.1.org/config-top.h   2009-12-23 05:29:39.000000000 +0900
--- bash-4.1/config-top.h       2014-09-29 10:13:15.201961360 +0900
***************
*** 103,109 ****
     bash_add_history() to be sent to syslog(). */
  /* #define SYSLOG_HISTORY */
  #if defined (SYSLOG_HISTORY)
! #  define SYSLOG_FACILITY LOG_USER
  #  define SYSLOG_LEVEL LOG_INFO
  #endif

--- 103,109 ----
     bash_add_history() to be sent to syslog(). */
  /* #define SYSLOG_HISTORY */
  #if defined (SYSLOG_HISTORY)
! #  define SYSLOG_FACILITY LOG_LOCAL6
  #  define SYSLOG_LEVEL LOG_INFO
  #endif
```

ログフォーマットは以下URLを参考に親プロセスIDとSUしたPID等を載せるように変えました。（監査的な追いやすさの需要のためです）
<a href="http://www.cprogramdevelop.com/3510088/" target="_blank">the syslog recorded history history - C - C Program Develop</a>

ファシリティがlocal6にかえてあります。`rsyslog.conf`で以下のようにする想定です。

```
*.info;mail.none;authpriv.none;cron.none;local6.none    /var/log/messages
# bash_history log
local6.*                                                /var/log/bash_history
```

別途yumのupdate対象からbashをはずす必要はあると思います。（`yum-updatesd`有効な場合は`yum.conf`で`exclude=bash*`するとか）
Chef(yum)で入れるときに、`options "--disablerepo=base,updates"`を入れる必要がありました。他リポジトリのパッケージでupdateされない対策です。他にはChefでレシピ流す時のrun_listの順序内でカスタムリポジトリを入れるレシピを先頭にしないとリポジトリが無いと判断されてinstallやupgradeされないという現象が起こりえます。

`RPMSign`というプラグインつかってgpg署名してカスタムyumリポジトリにscpしたりしてます。
gpg鍵とパスフレーズをシステム設定で設定しといて、
ジョブでは鍵選んでコマンドラインオプションのとこに署名するパッケージのフルパス書く(ワイルドカード可)感じです。

あとはscpするのとリポジトリのメタデータの差分作るジョブは別とか、
改ざん対策等でtd-agentでログサーバに内部ELB介して転送してcronで1日一回s3に送ってたりしますが、超長くなるので省略します。

jenkinsさんは自動的にいろいろしてくれるし暗黙の手順がなくなるので便利ですね。いろいろ勉強になりました。
jenkinsのことを社内の開発の方に教えていただきました。ありがとうございました。

では読んでいただいてありがとうございました。