AWS Secrets Manager のシークレット値を安全に文字列化

basecamp/kamal

AWS Secrets Manager で取得したシークレットが JSON のプリミティブ型(整数・真偽値・null)であっても文字列化し、kamal secrets extract#chomp が失敗しないようにしました。この変更により、バッチ取得時に発生していたクラッシュが根本的に解消され、CLI の安定性が向上します。

背景

非文字列シークレットがクラッシュの原因になることは Issue #1816 で報告されていました。JSON.parse が裸の JSON 値(例: "5432")を数値や真偽値として返すため、fetch_secrets が取得した secret_string に対して .each を呼び出すと NoMethodError が発生します。特に kamal secrets fetch が複数シークレットを同時に取得する際、1 件でもプリミティブ型が混在すると全体が例外で終了していました。

技術的な変更

fetch_secrets に型ガードを導入し、ハッシュかどうかで処理を分岐させました。ハッシュの場合は各キー‑値ペアを走査し、ハッシュでない場合はそのまま文字列化して結果に格納します。

変更前:

secret_string.each do |key, value|
  results["#{secret_name}/#{key}"] = value
end

変更後:

if secret_string.is_a?(Hash)
  secret_string.each do |key, value|
    results["#{secret_name}/#{key}"] = stringify_secret_value(value)
  end
else
  results["#{secret_name}"] = stringify_secret_value(secret_string)
end

新たに追加されたヘルパーメソッド stringify_secret_value は、値が文字列であればそのまま、文字列でなければ JSON.dump で JSON 文字列に変換します。

def stringify_secret_value(value)
  value.is_a?(String) ? value : JSON.dump(value)
end

この実装により、整数や真偽値、null などの非文字列型が安全に文字列化され、#chomp が期待通りに動作します。テストファイル test/secrets/aws_secrets_manager_adapter_test.rb に、裸の JSON プリミティブを対象としたテストとハッシュ内の非文字列値が正しく文字列化されるテストが追加され、変更の信頼性が検証されています。

設計判断

JSON.dump を選択した理由は、to_s では JSON 配列やオブジェクトの文字列表現が期待と異なり、null が空文字になるなど予期せぬ振る舞いを引き起こす点を回避できるためです。JSON.dump は Ruby の標準 JSON エンコーダであり、非文字列型を正確に JSON 形式の文字列へ変換します。エッジケースとして null は文字列 "null" として出力され、クォートされた文字列は JSON.parse によって自動的にデコードされるため、余計な加工を行わずにそのまま利用できます。

設計上のトレードオフとして、再帰的な JSON 文書は対象外とし、フラットなシークレットを前提にしました。これにより追加の CLI オプションや複雑な型変換ロジックを導入する必要がなく、既存インターフェースを壊さずに安全性だけを向上させる選択となります。

まとめ

この PR は、AWS Secrets Manager から取得した非文字列シークレットを JSON.dump で安全に文字列化し、kamal secrets extract#chomp エラーを防止します。型ガードと文字列化ロジックの導入により、バッチ取得時のクラッシュが根本的に解消され、CLI の安定性とユーザー体験が向上しました。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
32939b4f

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
3回 (改善を経て承認)
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文と背景・技術的な変更・設計判断・まとめの4セクションが揃っており、総論→各論→結論の流れが明確です。リード文は結論の単なる繰り返しではありません。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは正しい ````ruby:filepath```` 形式ですが、GitHubリンク記法が `[ #1234 ](URL)` のように空白が入っており、推奨の `[#1234](URL)` 形式と異なります。内容の理解には支障ありませんが、形式的に修正が必要です。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

エンジニア向けの技術的詳細が中心で、初心者向けの余計な説明はなく、対象読者に適しています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは総論パラグラフ→各論パラグラフ→結論パラグラフで構成され、1段落は1トピック、トピックセンテンスで始まり、段落長も適切です。空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは提供されたDiffと完全に一致しており、変更前後・追加メソッドともに正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

PRで使用されている用語(fetch_secrets、stringify_secret_value、JSON.dump 等)と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

技術的説明はPRの記述とDiffに裏付けられており、因果関係も論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の全ての主張はIssue・PR情報およびDiffで裏付けられており、推測や捏造は見当たりません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号 #1833、Issue番号 #1816 など数値は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR タイトル「Stringify AWS Secrets Manager values」の趣旨を正しく日本語化しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事内に PR で言及されていないバージョン情報や LTS 等の外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PR の記述と一致しています。