[{"data":1,"prerenderedAt":358},["ShallowReactive",2],{"blog-ja-k3s-sealed-secrets-gitops-rebuild-private-key-lost":3,"blog-related-ja-k3s-sealed-secrets-gitops-rebuild-private-key-lost":306,"blog-ja-k3s-sealed-secrets-gitops-rebuild-private-key-lost-alt":294},{"id":4,"title":5,"author":6,"body":7,"date":288,"description":289,"extension":290,"image":291,"locale":292,"meta":293,"navigation":294,"path":295,"seo":296,"stem":297,"tags":298,"__hash__":305},"blog\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost.md","GitOpsで全部コード化してたのに。K3sクラスタを作り直したら、Sealed Secretsが一つも復号できなくなった理由","Kubo Team",{"type":8,"value":9,"toc":278},"minimark",[10,15,23,26,34,45,49,55,67,88,95,99,105,121,130,134,140,151,159,163,169,172,222,234,243,250,254,257,265],[11,12,14],"h2",{"id":13},"_1-障害の発生-argocdは全部syncedなのにアプリだけが起動してこない","1. 障害の発生 — ArgoCDは全部Syncedなのに、アプリだけが起動してこない",[16,17,18],"p",{},[19,20],"img",{"alt":21,"src":22},"section01","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Fsection01.webp",[16,24,25],{},"K3sクラスタをGitOpsで作り直す作業は、本来「コードがすべて」のはずだった。Terraformでノードを立て、ArgoCDに向けてGitリポジトリを指せば、数十個のマニフェストが次々とSyncedになっていく。ところが、いくつかのアプリケーションだけがいつまで経ってもReadyにならない。ログを見ると、DBのパスワードやAPIトークンを読み込む段階でクラッシュしている。",[16,27,28,29,33],{},"調べていくと、原因は意外な場所にあった。",[30,31,32],"strong",{},"K3sクラスタを再構築したタイミングで、Sealed Secretsが一つも復号できない状態になっていた","のだ。Gitリポジトリには暗号化済みのSealedSecretリソースが正しくコミットされており、ArgoCD側の適用ログにもエラーはない。それでも、実際のSecretとして展開されない。GitOpsで「コード化」していたはずのシークレットだけが、再構築のたびに再現性を失っていた。",[16,35,36,37,44],{},"こうした「コード化したはずの運用が、再構築のたびに一部だけ再現できない」という落とし穴は、K3sクラスタを自前で積み上げるほど増えていく。",[38,39,43],"a",{"href":40,"rel":41},"https:\u002F\u002Fkubo.hexabase.io\u002F",[42],"nofollow","Kubo","のようなマネージドK3s環境を使うかどうかの分かれ目は、まさにこうした地味な運用設計の差にある。",[11,46,48],{"id":47},"_2-原因-sealed-secretsの秘密鍵はクラスタを作り直すたびに別物になる","2. 原因 — Sealed Secretsの秘密鍵は、クラスタを作り直すたびに\"別物\"になる",[16,50,51],{},[19,52],{"alt":53,"src":54},"section02","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Fsection02.webp",[16,56,57,58,62,63,66],{},"Sealed Secretsは、",[59,60,61],"code",{},"kubeseal"," CLIで平文のSecretを公開鍵暗号化し、",[59,64,65],{},"SealedSecret"," というCRDとしてGitにコミットできるようにする仕組みだ。クラスタ内で稼働するコントローラだけが対応する秘密鍵を持っており、SealedSecretリソースが適用されると、コントローラがそれを復号して通常のKubernetes Secretとして生成する。",[16,68,69,70,73,74,77,78,83,84,87],{},"ここで見落とされがちなのが、",[30,71,72],{},"その秘密鍵自体はGitに入っていない","という点だ。秘密鍵は",[59,75,76],{},"kube-system","Namespace内にある、ごく普通のKubernetes Secretとしてクラスタ内部にのみ存在する。",[38,79,82],{"href":80,"rel":81},"https:\u002F\u002Fraw.githubusercontent.com\u002Fbitnami-labs\u002Fsealed-secrets\u002Fmain\u002FREADME.md",[42],"公式リポジトリのドキュメント","にもある通り、この鍵は",[59,85,86],{},"kubectl get secret -n kube-system -l sealedsecrets.bitnami.com\u002Fsealed-secrets-key","で個別にバックアップしない限り、クラスタの外には一切出てこない。",[16,89,90,91,94],{},"今回のケースでは、K3sクラスタをゼロから新規構築し、GitOpsでマニフェストを流し込む運用だった。この手順では、新しいSealed Secretsコントローラが起動時に",[30,92,93],{},"まったく新しい鍵ペアを自動生成","する。Gitにコミットされていた既存のSealedSecretは、旧クラスタの鍵でしか復号できないため、新しい鍵を持つコントローラにとっては単なる暗号化されたゴミデータでしかない。GitOpsで「全部コード化」していても、鍵というコード化されない一点だけが引き継がれなかったのだ。",[11,96,98],{"id":97},"_3-k3sはetcdをバックアップしているから大丈夫という誤解","3. 「K3sはetcdをバックアップしているから大丈夫」という誤解",[16,100,101],{},[19,102],{"alt":103,"src":104},"section03","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Fsection03.webp",[16,106,107,108,116,117,120],{},"ここで整理しておきたいのが、K3sの標準的なバックアップ機構との関係だ。K3sは組み込みetcdデータストアを使う場合、",[38,109,112,115],{"href":110,"rel":111},"https:\u002F\u002Fdocs.k3s.io\u002Fdatastore\u002Fbackup-restore",[42],[59,113,114],{},"k3s etcd-snapshot","による定期スナップショット","をデフォルトで有効にしており、0時と12時にスナップショットを取得し5世代保持する。このスナップショットには秘密鍵を含むすべてのSecretが含まれているため、",[30,118,119],{},"etcdスナップショットから正しく復元した場合は、Sealed Secretsの鍵も一緒に復元され、問題は起きない","。",[16,122,123,124,129],{},"問題が起きるのは、今回のように「スナップショットからの復元」ではなく「新規にクラスタを構築してGitOpsで流し込む」運用のときだけだ。この違いを認識せずに「バックアップは取れているから安心」と考えていると、実際に再構築が必要になった瞬間に初めて気づくことになる。なお、K3sのドキュメントは",[38,125,128],{"href":126,"rel":127},"https:\u002F\u002Fdocs.k3s.io\u002Fcli\u002Fetcd-snapshot",[42],"データストアのスナップショットだけでなくサーバートークンファイルのバックアップも必須","としている点も見落とされがちな注意点だ。トークンが一致しないとスナップショット自体が使用不能になる。",[11,131,133],{"id":132},"_4-復旧手順-鍵のバックアップがあるかどうかで結果はまったく違う","4. 復旧手順 — 鍵のバックアップがあるかどうかで、結果はまったく違う",[16,135,136],{},[19,137],{"alt":138,"src":139},"section04","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Fsection04.webp",[16,141,142,143,146,147,150],{},"鍵のバックアップが残っていれば、復旧作業はシンプルだ。バックアップした鍵のYAMLを新クラスタに",[59,144,145],{},"kubectl apply","し、Sealed Secretsコントローラのポッドを再起動すれば、コントローラが既存の鍵を読み込み直し、Gitにある旧SealedSecretを正しく復号できるようになる。クラスタがダウンしている場合でも、",[59,148,149],{},"kubeseal --recovery-unseal","コマンドでオフラインでの復号が可能だ。",[16,152,153,154,158],{},"一方、鍵のバックアップが一つも残っていなかった場合は厳しい。",[38,155,157],{"href":80,"rel":156},[42],"Sealed Secretsのドキュメント","が明記している通り、鍵を失った状態でSealedSecretを復号する方法は存在しない。この場合、該当するシークレットについては、パスワードマネージャーやクラウドプロバイダーのシークレットストアなど、Git以外の経路で保管していた平文の値を手作業で再登録し、新しい鍵で再度シールし直すしかない。つまり、「シークレットの原本をGit以外のどこに控えているか」が、障害発生後の作業時間を大きく左右する。",[11,160,162],{"id":161},"_5-予防策-鍵のバックアップとローテーションを運用として仕組み化する","5. 予防策 — 鍵のバックアップとローテーションを運用として仕組み化する",[16,164,165],{},[19,166],{"alt":167,"src":168},"section05","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Fsection05.webp",[16,170,171],{},"再発を防ぐための打ち手は大きく3つある。",[173,174,175,192,198],"ul",{},[176,177,178,181,182,185,186,191],"li",{},[30,179,180],{},"鍵のバックアップを手順化する",": ",[59,183,184],{},"kubectl get secret -n kube-system -l sealedsecrets.bitnami.com\u002Fsealed-secrets-key -o yaml"," で取得したファイルを、Gitとは別のセキュアな保管場所（HashiCorp Vaultやオフラインの暗号化アーカイブなど）に保存する。",[38,187,190],{"href":188,"rel":189},"https:\u002F\u002Fwww.kubernetes.io\u002Fdocs\u002Fconcepts\u002Fsecurity\u002Fsecrets-good-practices\u002F",[42],"Kubernetes公式のSecretグッドプラクティス","でも、etcd上のSecretデータ自体の暗号化設定とあわせて、この種の鍵管理を「root権限のパスワードと同じ扱い」で管理することが推奨されている",[176,193,194,197],{},[30,195,196],{},"ローテーションの挙動を理解しておく",": Sealed Secretsのコントローラはデフォルトで30日ごとに新しい鍵ペアを生成するが、既存のSealedSecretを復号できるよう古い鍵も保持し続ける仕組みになっている。鍵のローテーションと、実際のシークレット値（パスワードなど）のローテーションは別物であることを運用チーム内で認識しておく",[176,199,200,203,204,209,210,215,216,221],{},[30,201,202],{},"復元経路そのものを見直す",": そもそも「新規構築+GitOps流し込み」という運用を避け、",[38,205,208],{"href":206,"rel":207},"https:\u002F\u002Fargo-cd.readthedocs.io\u002Fen\u002Fstable\u002Foperator-manual\u002Fsecret-management\u002F",[42],"ArgoCDの公式ドキュメント","が推奨する「デスティネーションクラスタでのシークレット管理」の考え方に沿って、",[38,211,214],{"href":212,"rel":213},"https:\u002F\u002Fexternal-secrets.io\u002Flatest\u002Fintroduction\u002Fgetting-started\u002F",[42],"External Secrets Operator","で",[38,217,220],{"href":218,"rel":219},"https:\u002F\u002Fdeveloper.hashicorp.com\u002Fvault\u002Ftutorials\u002Fkubernetes",[42],"HashiCorp Vault","のような外部シークレットストアと連携する構成に寄せるのも有効な選択肢だ。この構成なら、クラスタを作り直してもシークレットの実体は外部にあり、コントローラの鍵消失というリスク自体を切り離せる",[16,223,224,229,230,233],{},[38,225,228],{"href":226,"rel":227},"https:\u002F\u002Fwww.redhat.com\u002Fen\u002Fblog\u002Fa-guide-to-secrets-management-with-gitops-and-kubernetes",[42],"Red Hatのガイド","が指摘する通り、GitOpsにおけるシークレット管理には「暗号化してGitに置く」方式と「外部ストアへの参照だけをGitに置く」方式の二系統がある。どちらを選ぶにせよ、",[30,231,232],{},"「クラスタを作り直したときに何が引き継がれ、何が引き継がれないか」を事前に図示しておく","ことが、今回のような障害を防ぐ最も実効性のある一手になる。",[16,235,236,237,242],{},"こうした「バックアップは取っていたつもりが、実際に必要だった要素が欠けていた」という構造は、以前",[38,238,241],{"href":239,"rel":240},"https:\u002F\u002Fkubo.hexabase.io\u002Fblog\u002Fja\u002Fk3s-velero-backup-restore-local-path-provisioner-empty-pvc",[42],"VeleroでK3sのPVCを復元したら中身が空だった事例","でも触れた。GitOpsやバックアップの「仕組みがある」ことと、「実際に復元できる」ことの間には、常に確認すべきギャップが存在する。",[16,244,245,246,249],{},"K3sクラスタの運用設計そのものを自前で積み上げていくと、こうした鍵管理・バックアップ運用の落とし穴は増えていく。Prometheus+Grafanaのモニタリングやcert-managerの証明書自動更新を標準搭載している",[38,247,43],{"href":40,"rel":248},[42],"のように、こうした「地味だが致命的な運用設計」をあらかじめ組み込んだマネージドK3s環境を使うという選択肢も、検討する価値がある。",[11,251,253],{"id":252},"_6-まとめ","6. まとめ",[16,255,256],{},"K3sクラスタをGitOpsで再構築したときにSealed Secretsが全滅する原因は、秘密鍵がGitで管理されない「コード化の盲点」にあった。etcdスナップショットからの復元であれば鍵も引き継がれるが、新規構築+GitOps流し込みの運用では、コントローラが新しい鍵を自動生成してしまう。",[16,258,259,260,264],{},"対策は明確だ。鍵のバックアップをセキュアな場所に保管し、ローテーションの挙動を理解し、可能であれば",[38,261,263],{"href":206,"rel":262},[42],"ArgoCDが推奨する","外部シークレットストア連携への移行も視野に入れる。",[16,266,267,268,271,272,277],{},"もし、こうした鍵運用の設計・テストまで含めた本番運用を自前で抱えるのが負担に感じるなら、",[38,269,43],{"href":40,"rel":270},[42],"のようなマネージドK3s環境で、運用設計ごと任せてしまうという手もある。",[38,273,276],{"href":274,"rel":275},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[42],"お問い合わせ","から、現在の運用で抱えている課題を相談してみてほしい。",{"title":279,"searchDepth":280,"depth":280,"links":281},"",2,[282,283,284,285,286,287],{"id":13,"depth":280,"text":14},{"id":47,"depth":280,"text":48},{"id":97,"depth":280,"text":98},{"id":132,"depth":280,"text":133},{"id":161,"depth":280,"text":162},{"id":252,"depth":280,"text":253},"2026-10-15","K3sクラスタをGitOpsで再構築したらSealed Secretsが全て復号できない状態に陥った。原因は秘密鍵の扱いにある。障害の仕組みと復旧手順、再発防止の運用設計を解説する。","md","https:\u002F\u002Fcdn.kubo.hexabase.io\u002Fimages\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost\u002Ftitle.webp","ja",{},true,"\u002Fblog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost",{"title":5,"description":289},"blog\u002Fja\u002Fk3s-sealed-secrets-gitops-rebuild-private-key-lost",[299,300,301,302,303,304],"k3s","kubernetes","sealed-secrets","secrets-management","gitops","disaster-recovery","yXZCq_HTfpJr-AcZVeXc17nxetc68cAbd1i6ZMLE3Z0",[307,316,325,334,343,351],{"path":308,"title":309,"description":310,"date":311,"tags":312},"\u002Fblog\u002Fja\u002Fk3s-velero-backup-restore-local-path-provisioner-empty-pvc","毎晩\"Completed\"と出ていたVeleroのバックアップ。K3sでリストアしたらPVCが空だった理由","K3s標準のlocal-path-provisionerはCSIスナップショット非対応。Veleroのバックアップが\"Completed\"でも、障害時のリストアでPVCが空になることがある。原因の切り分けとFile System Backup・リストアドリルによる対策を解説する。","2026-10-13",[299,300,313,314,304,315],"velero","backup","csi",{"path":317,"title":318,"description":319,"date":320,"tags":321},"\u002Fblog\u002Fja\u002Fk3s-helmchart-crd-manual-helm-upgrade-reconcile-revert","K3sのHelmChart CRDで配ったアプリを直接helm upgradeしたら、次のreconcileで設定が消えた","K3sとKubernetesの違いは機能一覧だけでは分からない。HelmChart CRDという自動化レイヤーが、手動でのhelm upgradeやkubectl editを静かに巻き戻す仕組みと回避策を解説する。","2026-09-29",[299,300,322,323,303,324],"helm-controller","helmchart-crd","reconcile",{"path":326,"title":327,"description":328,"date":329,"tags":330},"\u002Fblog\u002Fja\u002Fk3s-etcd-snapshot-restore-token-cluster-reset-pitfalls","自動etcdスナップショットは毎日取れていた。K3sのリストアが本番障害で動かなかった理由","K3sのetcdスナップショットは正常に取れていたのに、本番障害でリストアが失敗した——トークン管理、S3リストア事故、cluster-resetの落とし穴という3つの実例から、embedded etcdバックアップの本当のリスクを解説する。","2026-09-21",[299,300,331,332,304,333],"etcd","backup-restore","high-availability",{"path":335,"title":336,"description":337,"date":338,"tags":339},"\u002Fblog\u002Fja\u002Fcicd-pipeline-devsecops-gate-design-four-layers","そのCI\u002FCDパイプライン、セキュリティチェックをどこに置くか決めずに継ぎ足していないか。DevSecOpsゲート設計の4層モデル","SASTもDASTもコンテナスキャンも導入した。それでも本番で事故は起きる。CI\u002FCDパイプラインのセキュリティを『4つの層』で設計し直すDevSecOpsゲート設計の考え方を、K3s\u002FKubernetes運用の観点から解説する。","2026-08-28",[299,300,340,341,342,303],"ci-cd","devsecops","admission-control",{"path":344,"title":345,"description":346,"date":347,"tags":348},"\u002Fblog\u002Fja\u002Fkubernetes-gitops-branch-antipattern-fleet-scaling","そのdev\u002Fstaging\u002Fprodブランチ、実は時限爆弾。KubernetesのGitOpsが壊れる本当の理由","dev\u002Fstaging\u002FproductionをGitブランチで分けるGitOps運用は、実はKubernetesの宣言的インフラの前提を壊すアンチパターンだ。ドリフトが起きる理由とディレクトリ構成・トランクベース運用への移行手順、フリート規模での崩壊を防ぐ設計を解説する。","2026-08-10",[299,300,303,349,350],"argocd","devops",{"path":352,"title":353,"description":354,"date":355,"tags":356},"\u002Fblog\u002Fja\u002Fkubernetes-image-signing-sigstore-supply-chain","イメージタグは誰でも書き換えられる。Kubernetesのイメージ署名にSigstoreで『来歴』を刻むという発想","コンテナイメージ署名の仕組みを解説。イメージタグは誰でも書き換え可能で、CI\u002FCDのテストを通過した保証にはならない。SigstoreとKyvernoを組み合わせ、Kubernetes\u002FK3s上で未署名イメージの起動を拒否する防御層を構築する方法を、GitOps運用との統合も含めて紹介する。","2026-08-06",[299,300,340,303,357],"security",1792592610851]