EKSにself-hosted runners(Github Actions)を立ち上げて安全にdigdag pushする

## はじめに

現在携わっているサービスではワークフローの実行にDigdagを採用しており、ワークフローのファイルはGithubで管理されています。この構成でGithub Actionsを使ってワークフローのデプロイを自動化したときに発生したセキュリティ課題と、その解決策についてまとめてみました。


## 前提

- DigdagのワークフローをGithubで管理している
- Github ActionsやCircleCIなどの外部からDigdagにPUSHをしたいと考えている。

[構成図]
今回の構成ではDigdagをEKS上で動かしています。
![digdag (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32289/5511011e-97f2-de4c-6aca-eca2015680f9.png)

## やりたいこと

- DigdagワークフローをGithub Actionsで自動デプロイすることで楽したい

## 課題
- セキュリティ観点からDigdagのエンドポイントは外部に公開したくないが、GithubActionsのIPからDigdagのエンドポイントに接続したい

## 解決策

Digdagのエンドポイントに接続する案としては2つ検討しました。

### 案1：RunnerのIPをSecurityGroupで許可

CircleCIではワークアラウンドとして一時的にCircleCIが利用するIPを許可する方法が紹介されています。

https://support.circleci.com/hc/ja/articles/115014372807-%E3%82%BB%E3%83%BC%E3%83%95%E3%83%AA%E3%82%B9%E3%83%88-%E3%83%9B%E3%83%AF%E3%82%A4%E3%83%88%E3%83%AA%E3%82%B9%E3%83%88%E3%81%AB%E6%8C%87%E5%AE%9A%E3%81%A7%E3%81%8D%E3%82%8B-%E9%9D%99%E7%9A%84IP-%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%AE%E7%AF%84%E5%9B%B2%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B-

GithubもIPアドレスの一覧を取得する方法が紹介されていますが、IPアドレスによる許可はお勧めしないと書かれています。

https://docs.github.com/ja/github/authenticating-to-github/about-githubs-ip-addresses

個人的にも共有されているIPを許可するのはお勧めしたくないかな、、、という印象です。

### 案2：self-hosted runnersを使う

今回はこちらの方法を採用しました。以下のような構成図になります。
![digdag (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32289/7a6c33ba-8818-7d8a-f6ee-f6703e7181be.png)

self-hosted runnersを使うメリットは以下のようなところだと思います。

- 定期的にGithub Actionsにポーリングする仕様になっているため、外部からself-hosted runnersにアクセスできる必要がない（セキュリティ的に有利）
- EKS上にPodで起動しておけば、外部に接続するときのIPアドレス(NatGatewayIP)が固定できる。
- DigdagエンドポイントのALB SecurityGroupにNatGatewayIPを登録しておけばself-hosted runnersからアクセスできる。

## やってみる

以下の手順については省略しています。

- Parsonal Access Tokenを取得する
- Personal Access TokenをSecretsに登録する（External Secretsを利用しています）
- NatGatewayのIPをALBのSecurityGroupに追加する


### Dockerfileを準備する

こちらのサイトを参考にさせていただき、一部書き換えています。
https://sanderknape.com/2020/03/self-hosted-github-actions-runner-kubernetes/

変更点

- stg環境とprd環境のrunnerをlabelで判定するために環境変数を追加
- digdagが動くようにベースイメージを変更（azul/zulu-openjdk:8）
- digdagをインストール

```dockerfile:Dockerfile
FROM azul/zulu-openjdk:8

ARG GITHUB_RUNNER_VERSION="2.277.1"

ENV RUNNER_NAME "runner"
ENV GITHUB_PAT ""
ENV GITHUB_OWNER ""
ENV GITHUB_REPOSITORY ""
ENV RUNNER_WORKDIR "_work"
ENV GLOBAL_ENVIRONMENT ""

RUN apt-get update \
    && apt-get install -y \
        curl \
        sudo \
        git \
        jq \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* \
    && useradd -m github \
    && usermod -aG sudo github \
    && echo "%sudo ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

RUN curl -o /usr/local/bin/digdag --create-dirs -L 'https://dl.digdag.io/digdag-0.10.0' \
    && chmod +x /usr/local/bin/digdag

USER github
WORKDIR /home/github

RUN curl -Ls https://github.com/actions/runner/releases/download/v${GITHUB_RUNNER_VERSION}/actions-runner-linux-x64-${GITHUB_RUNNER_VERSION}.tar.gz | tar xz \
    && sudo ./bin/installdependencies.sh

COPY --chown=github:github entrypoint.sh ./entrypoint.sh
RUN sudo chmod u+x ./entrypoint.sh

ENTRYPOINT ["/home/github/entrypoint.sh"]
```

```bash:entrypoint.sh
#!/bin/sh
registration_url="https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPOSITORY}/actions/runners/registration-token"
echo "Requesting registration URL at '${registration_url}'"

payload=$(curl -sX POST -H "Authorization: token ${GITHUB_PAT}" ${registration_url})
export RUNNER_TOKEN=$(echo $payload | jq .token --raw-output)

./config.sh \
    --labels ${GLOBAL_ENVIRONMENT} \
    --name $(hostname) \
    --token ${RUNNER_TOKEN} \
    --url https://github.com/${GITHUB_OWNER}/${GITHUB_REPOSITORY} \
    --work ${RUNNER_WORKDIR} \
    --unattended \
    --replace

remove() {
    ./config.sh remove --unattended --token "${RUNNER_TOKEN}"
}

trap 'remove; exit 130' INT
trap 'remove; exit 143' TERM

./run.sh "$*" &

wait $!
```

[注意点]
Parsonal Access Tokenを利用してrunner用のトークンを取得していますが、「Parsonal Access Token」は１年つかわないと削除されるため、1年以上Deployしていないとトークンが有効期限切れになりリリースに失敗する可能性があります。
https://docs.github.com/ja/github/authenticating-to-github/creating-a-personal-access-token

### ECRにイメージをPUSHする

今回はAmazonECRを利用するため事前にECRのリソースを作成しておきます。

```shell:コマンド例
docker build -t github-runner .
docker tag github-runner:latest xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/github-runner:latest
docker push xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/github-runner:latest
```

### デプロイ

Parsonal Access TokenはSecretに登録しておきます。

```yaml:github-runner.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: github-runner
  labels:
    app: github-runner
spec:
  replicas: 2
  selector:
    matchLabels:
      app: github-runner
  template:
    metadata:
      labels:
        app: github-runner
    spec:
      containers:
      - name: github-runner
        image: xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/github-runner:latest
        env:
        - name: GITHUB_OWNER
          value: 'xxxxxx' #組織名
        - name: GITHUB_REPOSITORY
          value: 'xxxxxxx' #GHAを動かすrepository
        - name: GLOBAL_ENVIRONMENT
          value: 'xxx' #stg/prdなど
        - name: GITHUB_PAT
          valueFrom:
            secretKeyRef:
              name: github-secret
              key: pat
```

```shell:kubernetesへのデプロイ
kubectl apply -f github-runner.yml
```

### 確認

GithubのSettings > ActionsにRunnerが登録されていれば正常に稼働しています。

![スクリーンショット 2021-04-08 18.38.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32289/4ee32078-0730-7cb9-a7ba-5bc7dcf3b039.png)