GitHub ActionからVPN経由でJenkins jobを実行したい

## はじめに
仕事でGitHubを使い出したんですが、Push後に既存のJenkins上にあるjobを実行をしたい。でもJenkinsサーバーはVPN上、どうやって繋ぐんだろう。
ってのを調べて解決したので記事に残しときます。

ポイントは3つ
1. GitHub上で秘密鍵などのシークレット情報はどうやって管理するんだろう？
2. VPNはどうやって張ったらいいんだろう？
3. GitHub ActionからどうやってJenkinsのJobを呼ぶんだろう？

## GitHubでのシークレット情報の管理
GitHubでは、リポジトリのsettins > secretからシークレット情報が保存できます。(リポジトリ管理者限定)

例えば `EncodedVPNSettings` という名前のsecuret情報を登録した場合、 `secrets.EncodedVPNSettings` を環境変数に設定することで、secret情報が使えるようになります。

```
    - name: Install Open VPN
      run: sudo apt-get install openvpn
    - name: Decode setting file
      env:
        ENC_SETTING: ${{ secrets.EncodedVPNSettings }}
```

詳細は公式マニュアルを見るのが一番分かりやすいです。[暗号化されたシークレットの作成と保存](https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets)

## VPNの貼り方

[こんなライブラリ](https://github.com/marketplace/actions/connect-vpn)もありましたが、もっとシンプルな方法を紹介。

**openvpnで使う設定ファイル一式を丸ごとsecretに保存する**

GitHub Actions上では以下のような処理を実行していきます。

1. tarで固めてbase64 encodingした設定ファイルをsecret情報に保存
2. openvpnをインストール
3. secret情報のbase64 encodingされたtarファイルをbase64デコードして一旦保存
4. tarファイルを解凍して利用する

注意としては、**secretはechoだとうまく参照できない**です。secret情報は表示が*に変わるようになっているからと思われます。
なので、pythonでファイルに書き込むなどする必要があります。

以下サンプルでは `EncodedVPNSettings` という名前で設定ファイルをsecretに登録しています。

```github_actions.yml
    - name: Install Open VPN
      run: sudo apt-get install openvpn
    - name: Decode setting file
      env:
        ENC_SETTING: ${{ secrets.EncodedVPNSettings }}
      run: |
        import os
        import subprocess
        import base64
        code=os.environ.get('ENC_SETTING')
        with open("tmp.tar.gz", 'w') as f:
          f.write(base64.b64decode(code))
      shell: python
    - name: Umcompress file
      run: |
        ls
        tar xvzf tmp.tar.gz
```

圧縮はこんな感じにスクリプトを用意してもいいかも

```encode.sh
if [ $# -ne 1 ]; then
        echo "$0 vpnpath"
        exit 0
fi
TOP=${1}
cd ${TOP}
tar czf .tmp.tar.gz *
echo "EncodedVPNSettings"
cat .tmp.tar.gz | base64
rm .tmp.tar.gz
cd - > /dev/null
```

## GitHub ActionからのJenkins jobの呼び方

GitHub Actionsから何か特別なことをするわけではなく、Jenkins job側で、外部実行可能な設定を行います。

Jenkins jobのconfigure、Build TriggersでTrigger builds remotelyを選び、Authentication Tokenを設定します。
その後は `JENKINS_URL/view/GitHub/job/JOBSNAME/buildWithParameters?token=TOKEN_NAME` をcurlなどで実行すればOK。

GitHub Actionsで利用する際は、このAuthentication Tokenをsecretで登録しましょう。以下サンプルではTokenNameという名前で登録しています。

```github_actions.yml
    - name: Call jenkins job
      env:
        TOKEN_NAME: ${{ secrets.TokenName }}
      run: |
        curl -gv --insecure http://localhost/view/GitHub/job/JOBSNAME/buildWithParameters?token=${TOKEN_NAME}
```

## GitHub Actionsコードサンプル

以下をrepositoryに設定した上で使用します。

|secret key|value|
|:---|:----|
|EncodedVPNSettings| tarで圧縮してbase64 encodingしたopenvpnの設定ファイル (+利用する鍵・証明書) |
|TokenName|jenkinsで設定したAutentication Token|

jenkinsはovervpnホストにあるsample_jobsという名前のjobの例です。

```github_actions.yml
name: github_actions
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - name: Clone code
      uses: actions/checkout@v2
    - name: Install Open VPN
      run: sudo apt-get install openvpn
    - name: Decode setting file
      env:
        ENC_SETTING: ${{ secrets.EncodedVPNSettings }}
      run: |
        import os
        import subprocess
        import base64
        code=os.environ.get('ENC_SETTING')
        with open("tmp.tar.gz", 'w') as f:
          f.write(base64.b64decode(code))
      shell: python
    - name: Umcompress file
      run: |
        ls
        tar xvzf tmp.tar.gz
    - name: Connect
      run: |
        CONF=`ls *.ovpn`
        sudo -b openvpn --config ${CONF}
    - name: Call jenkins job
      env:
        TOKEN_NAME: ${{ secrets.TokenName }}
      run: |
        curl -gv --insecure https://overvpn/view/GitHub/job/sample_jobs/buildWithParameters?token=${TOKEN_NAME}
    - name: kill vpn
      if: always()
      run: sudo killall openvpn
```
## 参考
[暗号化されたシークレットの作成と保存](https://help.github.com/ja/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets)
[JenkinsとGitHubのWebhook連携の整理](https://developer.aiming-inc.com/infra/jenkins-github-webhook-collaboration/#outline__1)
[Connect-VPN](https://github.com/marketplace/actions/connect-vpn)