GitHub Actions を Self-hosted Runner で動かす場合のセキュリティモニタリングどうすんの、というのを考えているメモ。

GitHub Actions をプライベートでも仕事でもバンバン使うようになっているが、サードパーティの Actions を使うのは抵抗がある。

https://docs.github.com/en/enterprise-server@3.0/actions/learn-github-actions/security-hardening-for-github-actions にあるようにハッシュでピン留めるとか、コードを監査して Fork して使うとか色々あるけど、限界があるし、CodeCov のようなケースも考えると Actions に限らず、CI 環境のセキュリティモニタリングって本番環境と同様にやる必要があるよね、というのが持論。

というわけで色々調べていて、最近話題になったのが GitLab の出した Package Hunter.

https://about.gitlab.com/blog/2021/07/23/announcing-package-hunter/

ここにあるように Falco を使ってモニタリングし、grpc API を通していい感じにイベントを扱うというツール。
この方式は自分も考えていて、eBPF なりを使ってコンテナのシステムコールを監査する。設計次第では安全なシステムコールとその引数などをリポジトリごとに定義できるのではないかと考えていた。別に Falco じゃなくても例えば sysdig でもいいし、コンテナを strace なりするとかでも良い。

自分はまだ Self-hosted Runner を自分で構築して、どういう動きをするのか知らないので、そこを試してみる。

Self-hosted Runner は https://docs.github.com/ja/actions/hosting-your-own-runners/adding-self-hosted-runners の手順に従うだけで構築できる。便利。

Docker を追加でインストールすると、次のような Workflow がシュッと動く。

on: workflow_dispatch

name: test

jobs:
  dispatch:
    name: test
    runs-on: self-hosted
    container: ubuntu:20.10

    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js 15.x
      uses: actions/setup-node@v1
      with:
          node-version: 15.x
    - run: |
                npm install

Workflow が実行されたログファイルは https://docs.github.com/ja/actions/hosting-your-own-runners/monitoring-and-troubleshooting-self-hosted-runners にあるように _diag ディレクトリにある。
Worker のログは3000行以上あって、まぁ全部読むのが大変だったので、どういうログが吐かれているかはちゃんと見ていない。

ちなみに、このときのコンテナの状況はこんな感じ。

mrtc0@sandbox:~$ docker ps
CONTAINER ID   IMAGE          COMMAND               CREATED          STATUS          PORTS     NAMES
ef9bb5a8e554   ubuntu:20.10   "tail -f /dev/null"   12 seconds ago   Up 10 seconds             8f33c7a78ed845628fb5f5fcefcca7e4_ubuntu2010_1ff3b7

コンテナの名前が 8f33c7a78ed845628fb5f5fcefcca7e4_ubuntu2010_1ff3b7 となっていて、最初のハッシュは commit Hash となっている。次がイメージ名で、1ff3b7 はちゃんと調べてない。

コンテナ生成のコマンドはこんな感じ。

/usr/bin/docker create --name 8f33c7a78ed845628fb5f5fcefcca7e4_ubuntu2010_1ff3b7 --label 78add5 --workdir /__w/actions-sandbox/actions-sandbox --network github_network_02e0fee3f9b14dad8ea1d6c8f6fc219e  -e "HOME=/github/home" -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/mrtc0/actions-runner/_work":"/__w" -v "/home/mrtc0/actions-runner/externals":"/__e":ro -v "/home/mrtc0/actions-runner/_work/_temp":"/__w/_temp" -v "/home/mrtc0/actions-runner/_work/_actions":"/__w/_actions" -v "/home/mrtc0/actions-runner/_work/_tool":"/__w/_tool" -v "/home/mrtc0/actions-runner/_work/_temp/_github_home":"/github/home" -v "/home/mrtc0/actions-runner/_work/_temp/_github_workflow":"/github/workflow" --entrypoint "tail" ubuntu:20.10 "-f" "/dev/null"

/var/run/docker.sock をマウントしているので rootful Docker だと下手すると root 取られますね。
リポジトリとかは結局 actions-runner のディレクトリをボリュームマウントしているだけっぽい。

Falco のログを見ると、ちゃんとコンテナの実行を検知している。

Aug 14 10:59:30 sandbox falco[11818]: 10:59:30.133623236: Notice Container with sensitive mount started (user=root user_loginuid=0 command=container:ef9bb5a8e554 8f33c7a78ed845628fb5f5fcefcca7e4_ubuntu2010_1ff3b7 (id=ef9bb5a8e554) image=ubuntu:20.10 mounts=/home/mrtc0/actions-runner/_work/_temp:/__w/_temp::true:rprivate,/home/mrtc0/actions-runner/_work/_actions:/__w/_actions::true:rprivate,/home/mrtc0/actions-runner/_work/_tool:/__w/_tool::true:rprivate,/home/mrtc0/actions-runner/_work/_temp/_github_home:/github/home::true:rprivate,/home/mrtc0/actions-runner/_work/_temp/_github_workflow:/github/workflow::true:rprivate,/var/run/docker.sock:/var/run/docker.sock::true:rprivate,/home/mrtc0/actions-runner/_work:/__w::true:rprivate,/home/mrtc0/actions-runner/externals:/__e:ro:false:rprivate)
Aug 14 10:59:30 sandbox falco: 10:59:30.133623236: Notice Container with sensitive mount started (user=root user_loginuid=0 command=container:ef9bb5a8e554 8f33c7a78ed845628fb5f5fcefcca7e4_ubuntu2010_1ff3b7 (id=ef9bb5a8e554) image=ubuntu:20.10 mounts=/home/mrtc0/actions-runner/_work/_temp:/__w/_temp::true:rprivate,/home/mrtc0/actions-runner/_work/_actions:/__w/_actions::true:rprivate,/home/mrtc0/actions-runner/_work/_tool:/__w/_tool::true:rprivate,/home/mrtc0/actions-runner/_work/_temp/_github_home:/github/home::true:rprivate,/home/mrtc0/actions-runner/_work/_temp/_github_workflow:/github/workflow::true:rprivate,/var/run/docker.sock:/var/run/docker.sock::true:rprivate,/home/mrtc0/actions-runner/_work:/__w::true:rprivate,/home/mrtc0/actions-runner/externals:/__e:ro:false:rprivate)

コンテナ名の先頭がハッシュぽかったら Actions のコンテナと判断してよさそう(どうせ専用ノードだろうし)。 あとは、このコンテナ名/IDとリポジトリ、さらにどの workflow かを特定できると便利なのだが、それは _diag 配下のログと照らし合わせるしかなさそうかなぁ。

ちょっとスマートのやり方を考え中…