1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

external-secrets: PushSecretを使ってKubernetesクラスター間でシークレットを共有する

Posted at

本記事では、external-secretsのPushSecret機能を使用して、Kubernetesクラスター間でシークレットを共有する方法を解説します。マルチクラスター環境での実運用において、シークレットの同期は重要な課題の一つです。クラスター間でシークレットを安全に共有し、自動的に同期する仕組みを構築することで、運用の効率化とセキュリティの向上を図ることができます。

実現したい構成

以下のような構成を実現し、ソースクラスターのシークレットをターゲットクラスターに自動的に同期します:

前提条件

本記事の手順を実行するには、以下のツールが必要です:

  • Docker
  • k3d
  • kubectl
  • Helm
  • kubectx(オプション: コンテキスト切り替えを簡単にするため)
  • kubectl-view-secret (オプション: シークレットの内容確認のため)

環境構築

k3dで次の2つクラスターを構築します。

  • ソースクラスター: k3d-source-cluster
  • ターゲットクラスター: k3d-target-cluster

構築手順は本記事の本質ではないものの、長くなってしまったので折りたたんであります↓

構築手順を見る

共有ネットワークの作成

まず、クラスター間で通信できるように共有ネットワークを作成します。このネットワークは、後ほど作成する2つのKubernetesクラスター間の通信を可能にします。

docker network create shared-net --subnet 172.28.0.0/16 --gateway 172.28.0.1

このコマンドで作成されるネットワークは以下の特徴を持ちます:

  • サブネット: 172.28.0.0/16
  • ゲートウェイ: 172.28.0.1
  • ネットワーク名: shared-net

ソースクラスターの構築

ソースクラスターは、シークレットを提供する側のクラスターです。以下の設定で構築します。

source-cluster.yamlを作成し、以下の内容を記述します:

apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
  name: source-cluster
servers: 1
agents: 1
image: docker.io/rancher/k3s:v1.30.0-k3s1
kubeAPI:
  host: 0.0.0.0
  hostIP: 127.0.0.1
  hostPort: "6443"
ports:
  - port: 8080:80
    nodeFilters:
      - loadbalancer
registries:
  create:
    name: registry.localhost
    host: 127.0.0.1
    hostPort: "15000"
network: shared-net
options:
  k3d:
    wait: true
  kubeconfig:
    updateDefaultKubeconfig: true
    switchCurrentContext: true
  k3s:
    extraArgs:
      # CIDR範囲の重複回避のため、クラスターごとに異なるCIDRを指定
      - arg: "--cluster-cidr=10.42.0.0/16"
        nodeFilters:
          - server:*
      - arg: "--service-cidr=10.43.0.0/16"
        nodeFilters:
          - server:*

この設定ファイルの重要なポイントを解説します:

  1. ネットワーク設定
    • KubeAPI: ポート6443でホストにバインド
    • ロードバランサー: ポート8080:80をマッピング
    • network: 先ほど作成した共有ネットワークを使用
  2. レジストリ設定
    • ローカルレジストリを作成(名前: registry.localhost)
    • ポート15000でホストにバインド
  3. CIDR設定
    • Cluster CIDR: 10.42.0.0/16
    • Service CIDR: 10.43.0.0/16
      (これらはクラスター間でのネットワークの重複を避けるために重要)

クラスターを作成するには、以下のコマンドを実行します:

k3d cluster create --config source-cluster.yaml

ターゲットクラスターの構築

ターゲットクラスターは、シークレットを受け取る側のクラスターです。ソースクラスターとは異なる設定で構築します。

target-cluster.yamlを作成し、以下の内容を記述します:

apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
  name: target-cluster
servers: 1
agents: 1
image: docker.io/rancher/k3s:v1.30.0-k3s1
kubeAPI:
  host: 0.0.0.0
  hostIP: 127.0.0.1
  hostPort: "6444"  # ソースクラスターと異なるポートを使用
ports:
  - port: 8081:80   # ソースクラスターと異なるポートを使用
    nodeFilters:
      - loadbalancer
registries:
  use:
    - registry.localhost  # ソースクラスターのレジストリを使用
network: shared-net
options:
  k3d:
    wait: true
  kubeconfig:
    updateDefaultKubeconfig: true
    switchCurrentContext: true
  k3s:
    extraArgs:
      # CIDR範囲の重複回避のため、ソースクラスターと異なるCIDRを指定
      - arg: "--cluster-cidr=10.44.0.0/16"
        nodeFilters:
          - server:*
      - arg: "--service-cidr=10.45.0.0/16"
        nodeFilters:
          - server:*

この設定ファイルのポイントを解説します:

  1. ソースクラスターとの違い
    • KubeAPIポート: 6444(ソースクラスターは6443)
    • ロードバランサーポート: 8081:80(ソースクラスターは8080:80)
    • CIDR範囲: 10.44.0.0/16と10.45.0.0/16(ソースクラスターとは異なる範囲)
  2. レジストリ設定
    • ソースクラスターで作成したレジストリを使用
    • use セクションで既存のレジストリを指定
  3. ネットワーク設定
    • ソースクラスターと同じ共有ネットワークを使用
    • これによりクラスター間の通信が可能に

クラスターを作成:

k3d cluster create --config target-cluster.yaml

クラスター間通信の検証

コンテキストの確認

まず、両方のクラスターが正しく作成されたことを確認します:

kubectl config get-contexts

出力例:

CURRENT   NAME                 CLUSTER              AUTHINFO                   NAMESPACE
          k3d-source-cluster   k3d-source-cluster   admin@k3d-source-cluster   
*         k3d-target-cluster   k3d-target-cluster   admin@k3d-target-cluster   

コンテキストの切り替え方法:

# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster

# ターゲットクラスターに切り替え
kubectl config use-context k3d-target-cluster

クラスター間の疎通確認

クラスター間の通信を確認するため、デバッグ用のPodを作成します。

1. デバッグ用Pod定義の作成

debug-pod.yamlを作成:

apiVersion: v1
kind: Pod
metadata:
  name: debug-pod
spec:
  containers:
  - name: debug
    image: curlimages/curl
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: auth
      mountPath: /tmp/auth
      readOnly: true
  volumes:
  - name: auth
    secret:
      secretName: debug-auth

このPodの特徴:

  • 軽量なcurlイメージを使用
  • 無限スリープで実行し続ける
  • 認証情報をボリュームとしてマウント

2. 認証情報の準備

debug-auth-secret.yamlを作成:

apiVersion: v1
kind: Secret
metadata:
  name: debug-auth
type: Opaque
data:
  client-certificate-data: ... # kubeconfigのtarget-clusterのBase64エンコードされたclient-certificate-data
  client-key-data: ... # kubeconfigのtarget-clusterのBase64エンコードされたclient-key-data

認証情報の取得方法:

# kubeconfigの内容を表示
kubectl config view --raw

Base64エンコードされた認証情報をコピーして、debug-auth-secret.yamlに貼り付けます。

3. デバッグ環境のセットアップ

# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster

# 認証情報とデバッグPodを作成
kubectl apply -f debug-auth-secret.yaml
kubectl apply -f debug-pod.yaml

# Podの状態を確認
kubectl get pod debug-pod

4. 疎通確認の実行

kubectl exec debug-pod -- curl -k \
  --cert /tmp/auth/client-certificate-data \
  --key /tmp/auth/client-key-data \
  https://k3d-target-cluster-server-0:6443/api/v1/namespaces

このコマンドで成功すると、JSONフォーマットでnamespaceのリストが返ってきます。これは以下を意味します:

  • ターゲットクラスターのAPIサーバーに到達可能
  • 認証が正常に機能している
  • クラスター間の基本的な通信が確立されている

external-secretsのセットアップ

クラスターの準備が確認できたら、いよいよexternal-secretsのセットアップを行います。

ソースクラスターへのインストール

# ソースクラスターのコンテキストに切り替え
kubectl config use-context k3d-source-cluster

helm repo add external-secrets https://charts.external-secrets.io
helm repo update

helm install external-secrets \
   external-secrets/external-secrets \
    -n external-secrets \
    --create-namespace

ターゲットクラスターの認証情報設定

ソースクラスターからターゲットクラスターへのアクセスに必要な認証情報を設定します。

認証情報の取得

まず、ターゲットクラスターのクライアント証明書情報を取得します:

kubectl config view --raw

# 以下の情報をメモ
# - client-certificate-data
# - client-key-data
# - certificate-authority-data

認証情報の設定

target-cluster-credentials.yamlを作成:

apiVersion: v1
kind: Secret
metadata:
  name: target-cluster-credentials
  namespace: default
type: Opaque
data:
  client-certificate-data: ... # kubeconfigから取得したBase64エンコード済みデータ
  client-key-data: ... # kubeconfigから取得したBase64エンコード済みデータ

認証情報をソースクラスターに適用:

# ソースクラスターのコンテキストに切り替え
kubectl config use-context k3d-source-cluster

# 認証情報の適用
kubectl apply -f target-cluster-credentials.yaml

# 作成されたSecretの確認
kubectl get secret target-cluster-credentials -o yaml

SecretStoreの設定

SecretStoreは、external-secretsがシークレットを保存・取得するためのバックエンドストアを定義します。

SecretStore定義の作成

secret-store.yamlを作成:

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: target-cluster
  namespace: default
spec:
  provider:
    kubernetes:
      remoteNamespace: default  # ターゲットクラスターのnamespace
      server:
        url: https://k3d-target-cluster-server-0:6443  # k3d内部のホスト名を使用
        # CA証明書はBase64エンコードされた値を直接指定
        caBundle: ... # kubeconfigから取得したcertificate-authority-data
      auth:
        cert:
          clientCert:
            name: target-cluster-credentials
            key: client-certificate-data
          clientKey:
            name: target-cluster-credentials
            key: client-key-data

SecretStoreの適用と確認

# SecretStoreの適用
kubectl apply -f secret-store.yaml

# 状態の確認
kubectl describe secretstore target-cluster

# 期待される出力に含まれる内容
Status:
  Conditions:
    Last Transition Time:  ...
    Message:               SecretStore validated
    Reason:               Valid
    Status:               True
    Type:                 Ready

PushSecretの設定と動作確認

PushSecretの作成

PushSecretは、ソースクラスターのシークレットをターゲットクラスターに自動的にプッシュするための設定です。

サンプルシークレットの作成(ソースクラスター)

# サンプルシークレットの作成
kubectl create secret generic my-secret \
  --from-literal=username=admin \
  --from-literal=password=supersecret

# 作成されたシークレットの確認
kubectl get secret my-secret -o yaml

PushSecret定義の作成

push-secret.yamlを作成:

apiVersion: external-secrets.io/v1alpha1
kind: PushSecret
metadata:
  name: pushsecret-example
  namespace: default
spec:
  # 同期時にプロバイダーの既存のシークレットを上書き
  updatePolicy: Replace
  # PushSecret削除時にプロバイダーのシークレットも削除
  deletionPolicy: Delete
  # 再同期の間隔
  refreshInterval: 10s
  # シークレットをプッシュするSecretStore
  secretStoreRefs:
    - name: target-cluster
      kind: SecretStore
  # 同期対象のSecret
  selector:
    secret:
      name: my-secret  # ソースクラスターでのSecretのname
  data:
    - match:
        secretKey: username  # ソースクラスターでのSecretのkey
        remoteRef:
          remoteKey: my-secret-copy  # ターゲットクラスターでのSecretのname
          property: username-copy    # ターゲットクラスターでのSecretのkey
    - match:
        secretKey: password  # ソースクラスターでのSecretのkey
        remoteRef:
          remoteKey: my-secret-copy  # ターゲットクラスターでのSecretのname
          property: password-copy    # ターゲットクラスターでのSecretのkey

PushSecretの適用

kubectl apply -f push-secret.yaml

# 状態の確認
kubectl describe pushsecret pushsecret-example

動作確認

ターゲットクラスターでのシークレット確認

# ターゲットクラスターに切り替え
kubectl config use-context k3d-target-cluster

# シークレットの確認
kubectl describe secret my-secret-copy

# 期待される出力
Name:         my-secret-copy
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password-copy:  11 bytes
username-copy:  5 bytes

シークレットの内容確認

シークレットの値を確認するには、以下のようなコマンドを使用できます:

# kubectl-view-secretプラグインを使用する場合
kubectl-view-secret my-secret-copy --all

# または、直接Base64デコードする場合
kubectl get secret my-secret-copy -o jsonpath='{.data.username-copy}' | base64 -d
kubectl get secret my-secret-copy -o jsonpath='{.data.password-copy}' | base64 -d

期待される出力:

password-copy='supersecret'
username-copy='admin'

自動同期の確認

ソースクラスターでシークレットを更新し、ターゲットクラスターに反映されることを確認:

# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster

# シークレットの更新
kubectl create secret generic my-secret \
  --from-literal=username=newadmin \
  --from-literal=password=newsecret \
  --dry-run=client -o yaml | kubectl apply -f -

# ターゲットクラスターに切り替えて確認
kubectl config use-context k3d-target-cluster
kubectl-view-secret my-secret-copy --all

クリーンアップ

PushSecretの削除

# ソースクラスターに切り替え
kubectl config use-context k3d-source-cluster

# PushSecretの削除
kubectl delete pushsecret pushsecret-example

# ターゲットクラスターでシークレットが削除されたことを確認
kubectl config use-context k3d-target-cluster
kubectl get secret my-secret-copy
# "Error from server (NotFound): secrets "my-secret-copy" not found" と表示されれば成功

クラスターの削除

# クラスターの削除
k3d cluster delete --config source-cluster.yaml
k3d cluster delete --config target-cluster.yaml

# 共有ネットワークの削除
docker network rm shared-net

まとめ

本記事では、external-secretsのPushSecret機能を使用して、Kubernetesクラスター間でシークレットを共有する方法を詳しく解説しました。実運用では、この機能を活用することで、マルチクラスター環境でのシークレット管理を効率化できると思います!

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?