セルフホステッドランナーでvault-actionを試す
Github ActionsでCI/CDする際に必要なシークレットを
全部リポジトリごとにGithub上で管理するのはなかなか大変ですし、
外部に機密情報を保存するのはなるべく避けたいものです。
というわけでオンプレに構築したvaultから、
同じくオンプレで起動するセルフホステッドランナーで
vault-action
を使いシークレット情報を取得したいと思います。
環境
色々いじってたら壊れたのでRancherをアップグレードして、
ダウンストリートクラスターを再構築しています。
■ Rancher Cluster
root@rancher:~# kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
rancher Ready control-plane,etcd,master 338d v1.32.5+k3s1 192.168.0.208 <none> Ubuntu 24.04 LTS 6.8.0-51-generic containerd://2.0.5-k3s1.32
■ Downstream Cluster
このクラスタはRaspberry Piで構築しているのでarm64環境です。
$ k get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s1 Ready control-plane,etcd,master 33h v1.32.5+rke2r1 192.168.0.51 <none> Ubuntu 24.04.2 LTS 6.8.0-1028-raspi containerd://2.0.5-k3s1
k8s2 Ready worker 33h v1.32.5+rke2r1 192.168.0.52 <none> Ubuntu 24.04.2 LTS 6.8.0-1018-raspi containerd://2.0.5-k3s1
k8s3 Ready worker 33h v1.32.5+rke2r1 192.168.0.53 <none> Ubuntu 24.04.2 LTS 6.8.0-1018-raspi containerd://2.0.5-k3s1
Downstream Clusterにvault serverとActions Runner Controllerをインストールしています。
それぞれの構築手順は以下をみてください。
ラズパイk8sにVault serverを構築
ラズパイk8sにself-hosted runnersをActions Runner Controllerでインストール
ubuntu@k8s1:~$ k get pod,ingress -n vault
NAME READY STATUS RESTARTS AGE
pod/vault-0 1/1 Running 0 33h
pod/vault-1 1/1 Running 0 33h
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/vault <none> vault.tsuchinokometal.com 192.168.0.52,192.168.0.53 80 33h
ubuntu@k8s1:~$ k get pod -n arc-systems
NAME READY STATUS RESTARTS AGE
arc-gha-rs-controller-78b5f4b45b-62rhb 1/1 Running 0 31h
arc-runner-set-754b578d-listener 1/1 Running 0 31h
ちなみにバージョンは以下の通り。
ubuntu@k8s1:~$ vault status | grep Version
Version 1.19.0
ubuntu@k8s1:~$ helm list -n arc-systems
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
arc arc-systems 1 2025-06-15 13:53:17.434598985 +0900 JST deployed gha-runner-scale-set-controller-0.12.0 0.12.0
ubuntu@k8s1:~$ helm list -n arc-runners
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
arc-runner-set arc-runners 1 2025-06-15 13:55:31.506930496 +0900 JST deployed gha-runner-scale-set-0.12.0 0.12.0
tokenを使ってシークレットを取得する
簡単な方法としては vault token ですかね
試しに以下のワークフローを作成しました。
動きとしてはkubeconfigファイルの内容をvaultに登録して、
vault-actionで取得しそれを使って別クラスタである
rancher clusterに対してkubectlを実行するというものです。
なぜわざわざこんなことをしているかというと、
取得したシークレットは全部自動でマスクされるので単純にechoで確認できないからです!
name: kubectl demo
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
kubectl-demo:
runs-on: arc-runner-set
env:
KUBE_VERSION: "1.32.5"
steps:
- name: Import Secrets
id: import-secrets
uses: hashicorp/vault-action@v3.4.0
with:
token: ${{ secrets.VAULT_TOKEN }}
url: http://vault.tsuchinokometal.com
exportEnv: false
secrets: arc-runners/data/kubeconfig/rancher-cluster config | KUBECONFIG_B64
- name: Setup kubectl
run: |
curl -LO https://dl.k8s.io/release/v${{ env.KUBE_VERSION }}/bin/linux/arm64/kubectl
chmod +x ./kubectl
mkdir -p ~/.local/bin
mv ./kubectl ~/.local/bin/kubectl
echo "PATH=$PATH:~/.local/bin" >> $GITHUB_ENV
- name: Setup kubeconfig
run: |
echo "${{ steps.import-secrets.outputs.KUBECONFIG_B64 }}" | base64 -d > /tmp/kubeconfig
echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV
- name: Get node
run: |
kubectl get node -o wide
解説します。
事前にRepository secretsでVAULT_TOKEN作成し、
token: ${{ secrets.VAULT_TOKEN }}
で読み込んでいます。
ひとまずテストとしてvault operator init実行時に生成されるルートトークンを使いました。
なんでもできちゃうやつですね。
vaultへのシークレット登録は以下のような感じです。
kubeconfig-rancher-cluster.yamlがkubeconfigファイルです。
base64でエンコードしないとうまくいかなかったです。
ubuntu@k8s1:~$ vault secrets enable -path=arc-runners -description="github app for arc" kv-v2
Success! Enabled the kv-v2 secrets engine at: arc-runners/
ubuntu@k8s1:~$ cat kubeconfig-rancher-cluster.yaml | base64 | vault kv put -mount=arc-runners kubeconfig/rancher-cluster config=-
=============== Secret Path ===============
arc-runners/data/kubeconfig/rancher-cluster
======= Metadata =======
Key Value
--- -----
created_time 2025-06-16T12:28:30.566613147Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
このdataを含んだSecret Pathで指定します。
Setup kubeconfigのstepで取得したシークレットをファイルに出力しています。
KUBECONFIGという環境変数で指定すればkubectlで読み込んでくれるのでそうしている感じです。
outputsで読み込んでいますが、デフォルトだと環境変数でも設定してくれます。
ただそうするとマスクされたenvが複数行ログに残るのがちょっと嫌なので
今回はexportEnv: falseにしています。
Setup kubectlのstepでkubectlをインストールしています。
Azure/setup-kubectl
がarm64環境だとうまく動かなかったので、
まんま
こちら
の手順をやっているだけです。
実行結果が以下です。
無事kubectlが実行できていますね。

kubrernetes認証を使ってシークレットを取得する
外部からアクセスできないとはいえ、ルートトークンを外部に登録するのは嫌ですね。
今回構築したrunnerはkubernetesクラスタで起動するので
kubernetes認証
を試してみます。
今回はvault側の設定は以下のようにしました。
※こちら
とかも参考になると思います。
ubuntu@k8s1:~$ vault auth enable --path=my-cluster/arc-runners kubernetes
Success! Enabled kubernetes auth method at: my-cluster/arc-runners/
ubuntu@k8s1:~$ vault write auth/my-cluster/arc-runners/config kubernetes_host="https://kubernetes.default.svc.cluster.local:443"
Success! Data written to: auth/my-cluster/arc-runners/config
ubuntu@k8s1:~$ vault policy write arc-runners-policy - <<EOF
path "arc-runners/data/kubeconfig/*" {
capabilities = ["read"]
}
EOF
ubuntu@k8s1:~$ vault write auth/my-cluster/arc-runners/role/actions bound_service_account_names=arc-runner-set-gha-rs-no-permission bound_service_account_namespaces=arc-runners policies=arc-runners-policy ttl=20m
Success! Data written to: auth/my-cluster/arc-runners/role/actions
kubernetes_hostはrunnerとvaultが同じクラスタにいるのでclusterIPを指定しています。
roleはactionsという名前にしてサービスアカウントは
runner scale setをhelmでインストールするとデフォルトで
このサービスアカウントで起動していたのでそれを指定しています。
Actionsワークフローは以下のようになります。
name: kubectl demo
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
kubectl-demo:
runs-on: arc-runner-set
env:
KUBE_VERSION: "1.32.5"
steps:
- name: Import Secrets
id: import-secrets
uses: hashicorp/vault-action@v3.4.0
with:
method: kubernetes
url: http://vault.tsuchinokometal.com
path: my-cluster/arc-runners
role: actions
exportEnv: false
secrets: arc-runners/data/kubeconfig/rancher-cluster config | KUBECONFIG_B64
- name: Setup kubectl
run: |
curl -LO https://dl.k8s.io/release/v${{ env.KUBE_VERSION }}/bin/linux/arm64/kubectl
chmod +x ./kubectl
mkdir -p ~/.local/bin
mv ./kubectl ~/.local/bin/kubectl
echo "PATH=$PATH:~/.local/bin" >> $GITHUB_ENV
- name: Setup kubeconfig
run: |
echo "${{ steps.import-secrets.outputs.KUBECONFIG_B64 }}" | base64 -d > /tmp/kubeconfig
echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV
- name: Get node
run: |
kubectl get node -o wide
methodをkubernetesにして、
pathとroleをvault側の設定に合わせています。
うまくいけばtokenの時と同じ結果になるはずです。
JWT with OIDC Providerを使ってシークレットを取得する
OIDC
も試してみました。
こちらのドキュメント
を参考に進めます。
またこちら
も参考にさせていただきました。
ubuntu@k8s1:~$ vault auth enable jwt
Success! Enabled jwt auth method at: jwt/
ubuntu@k8s1:~$ vault write auth/jwt/config \
bound_issuer="https://token.actions.githubusercontent.com" \
oidc_discovery_url="https://token.actions.githubusercontent.com"
Success! Data written to: auth/jwt/config
vault write auth/jwt/role/actions -<<EOF
{
"role_type": "jwt",
"user_claim": "actor",
"bound_audiences": "https://github.com/<username>",
"bound_claims": {
"repository": "<username>/<repo_name>"
},
"policies": ["arc-runners-policy"],
"ttl": "10m"
}
EOF
<username>と<repo_name>は自身の環境に書き換えてください。
policyはkubernetes認証で作成したものを利用しています。
以下のエラーが出て悩んだのですが、bound_audiencesを加えることで解消しました。
個人アカウントで試しているのでこうしていますが、
組織ですと"https://github.com/<org_name>“になるんじゃないかと思います。
failed to retrieve vault token. code: ERR_NON_2XX_3XX_RESPONSE, message: Response code 400 (Bad Request), vaultResponse: {"errors":["audience claim found in JWT but no audiences bound to the role"]}
Actionsワークフローは以下のようになります。
name: kubectl demo
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
kubectl-demo:
permissions:
id-token: write
contents: read
runs-on: arc-runner-set
env:
KUBE_VERSION: "1.32.5"
steps:
- name: Import Secrets
id: import-secrets
uses: hashicorp/vault-action@v3.4.0
with:
method: jwt
url: http://vault.tsuchinokometal.com
role: actions
exportEnv: false
secrets: arc-runners/data/kubeconfig/rancher-cluster config | KUBECONFIG_B64
- name: Setup kubectl
run: |
curl -LO https://dl.k8s.io/release/v${{ env.KUBE_VERSION }}/bin/linux/arm64/kubectl
chmod +x ./kubectl
mkdir -p ~/.local/bin
mv ./kubectl ~/.local/bin/kubectl
echo "PATH=$PATH:~/.local/bin" >> $GITHUB_ENV
- name: Setup kubeconfig
run: |
echo "${{ steps.import-secrets.outputs.KUBECONFIG_B64 }}" | base64 -d > /tmp/kubeconfig
echo "KUBECONFIG=/tmp/kubeconfig" >> $GITHUB_ENV
- name: Get node
run: |
kubectl get node -o wide
ドキュメントにも書いてありますが、permissionsを忘れないようにしてください。
うまくいけばtokenの時と同じ結果になるはずです。
いやーセルフホステッドランナーだと
料金を気にせずトライアンドエラーできるので良いですねー