スキップしてメイン コンテンツに移動

【Day 24】docker-compose.yml のGit共有と .env のGit除外ルール 〜パスワード・接続文字列の漏洩を防ぐ安全なチーム開発とC#環境変数注入設計〜

Day 24 アイキャッチ
▲ Day 24: docker-compose.yml のGit共有と .env のGit除外ルール
🔐
受講生の皆さん、お疲れ様です! Docker/コンテナ実践講座の専任講師です。
前回【Day 23】では、Web API + SQL Server + Redis という本格的なモダン3層コンテナ構成をDocker Composeで立ち上げました。
チーム開発でこの便利な環境を共有しようとした時、絶対に避けて通れない最重要テーマが「機密情報(シークレット)のセキュリティ管理」です!
YAMLファイルに書かれたデータベースのパスワードや接続文字列を、そのままGitにコミットしてしまっていませんか?
本日【Day 24】は、設計図(docker-compose.yml)を安全にチーム共有しながら、パスワードを完全にGitから隔離する「.env と .gitignore によるセキュリティ運用」と、「C#(IConfiguration)への環境変数注入設計」を徹底解説します!

① 導入・本日の達成目標

「docker-compose.yml が便利だから、Gitリポジトリにプッシュしてチームメンバーにも共有しよう!」
そう思った瞬間、ふと手が止まった経験はありませんか?

⚠️ 現場で頻発する危険なシチュエーション:
MSSQL_SA_PASSWORD: "MyStrongPassword123!" と書かれたComposeファイルを、そのままGitリポジトリへコミット&プッシュ。
その瞬間、GitHubや社内GitLabの全閲覧者に対してデータベースの管理者パスワードが丸見えになり、コミット履歴(git log)にも永久に刻み込まれてしまう…!

オンプレミスや従来のWindowsサーバー運用では、サーバー上の web.config や構築手順書のExcelにパスワードを直接書き込み、閉じたネットワーク内で管理していました。
しかし、インフラ構成をコードで管理するIaC(Infrastructure as Code)やGit中心のチーム開発においては、この旧来の感覚のまま作業すると「即座に重大なセキュリティインシデント」へと直結します。

🎯 本日の到達目標(ゴール宣言)

  • 平文コミットの危険性とGitの履歴性質: なぜコードやYAMLにパスワードを直書きしてはいけないのか、漏洩のメカニズムを理解する。
  • Gitコミット対象 vs 除外の境界線: docker-compose.yml / .env.example(Git管理)と .env(.gitignore除外)の完璧な棲み分けを身につける。
  • Docker Compose の変数展開仕様: .env ファイルがComposeによって自動読み込みされ、${VARIABLE} に置換される仕組みをマスターする。
  • C#(ASP.NET Core)へのセキュア設定注入: appsettings.json のデフォルト値を、コンテナ実行時の環境変数(ConnectionStrings__*)で透過的にオーバーライドする実践コードを習得する。

② パスワード平文コミットの恐怖 vs .env による安全管理(パラダイムシフト)

まずは、なぜGit環境においてパスワードの取り扱いにこれほど神経を尖らせる必要があるのか、その本質的な理由を図解で確認しましょう。

パスワード平文コミットの危険性 vs .env 安全管理
▲ 【図解①】パスワード平文コミットの恐怖 vs .env による安全管理(Git履歴に残る漏洩事故と環境変数分離の安全性)

Gitコミット履歴の「不変性」という罠

「もし間違えてパスワードをコミットしても、すぐにファイルからパスワードを消して、もう一度コミット(修正コミット)すれば大丈夫なのでは?」
これは初心者やGitに不慣れなエンジニアが最も陥りやすい致命的な誤解です。

Gitは、すべてのコミット時点のスナップショットを暗号学的ハッシュ値(SHA-1/SHA-256)で厳密にチェーン化して保持する分散バージョン管理システムです。
最新の HEAD コミットでファイル内のパスワードを削除しても、過去のコミット履歴(git log や git show)を開けば、過去にプッシュされたファイルの内容は1文字の狂いもなく閲覧・復元可能です。

現代のGitHubやGitLabには、リポジトリにプッシュされたコードを自動スキャンしてパスワードやAPIトークンを検知する「Secret Scanning」機能が備わっています。
パブリックリポジトリに誤ってコミットした場合、数秒以内に世界中の自動クローラーに拾われ、悪用されてしまうのが今のサイバーセキュリティの過酷な現実です。

比較項目 ❌ 危険: 平文ベタ書き&コミット ⭕ 安全: .env による環境分離
パスワードの所在 YAMLやコード内に直接ハードコード ローカル端末の .env ファイル内のみ
Gitリポジトリ共有 パスワードごと全員・全履歴に共有(危険) プレースホルダー(${変数})のみ共有(安全)
環境ごとの切り替え 開発/本番でファイルを書き換え事故多発 環境ごとの .env を差し替えるだけで完結
漏洩時の影響 全リポジトリ閲覧者・Git履歴から永続漏洩 ローカル漏洩なし(Git上に実値が一切存在しない)

③ Gitコミット対象 vs .gitignore 除外の徹底分類

では、マルチコンテナ連携プロジェクトにおいて、「何をGitで共有し、何を各自のPC内に隔離すべきなのか」、その境界線を明確に定義しましょう。

Gitコミット対象 vs .gitignore 除外の徹底分類
▲ 【図解②】Gitコミット対象(共有) vs .gitignore 除外(秘匿)の徹底分類カード(.env.example 運用の真髄)

1. Gitコミットすべきファイル(チームの設計図)

  • docker-compose.yml: 複数コンテナの構成、ポートマッピング、ボリューム定義、サービス依存関係(depends_on)などのアーキテクチャ設計図です。パスワード値そのものは書かず、後述する ${SA_PASSWORD} 形式の環境変数参照で記述し、全員で共有します。
  • .env.example(超重要!運用の要): 本物のパスワードではなく、「このプロジェクトを動かすためにどんなキー名の環境変数が必要か」を示したひな型(テンプレート)ファイルです。
    値には YourStrongPasswordHere! や localhost:1433 といったダミー値・説明用サンプル値を記述してGit管理します。
  • Dockerfile / .dockerignore / ソースコード: 当然ながらGit管理対象です。

2. 絶対にGitコミットしてはいけないファイル(秘匿情報)

  • .env(実値ファイル): 開発者個人のローカルパスワード、本番DBの管理者パスワード、クラウド接続文字列、外部API秘密鍵などの実値が記載されたファイルです。
    絶対に .gitignore に追加し、Gitの追跡対象から完全に除外しなければなりません!
  • .env.local / .env.production: 特定環境用の実値ファイルも同様にGit除外対象です。

📁 チーム開発における黄金のワークフロー

  1. プロジェクト作成者は .env.example を作成してGitコミットする。
  2. リポジトリルートの .gitignore に .env を忘れずに追記する。
  3. 新しい開発者が git clone したら、まず cp .env.example .env(Windowsなら copy .env.example .env)を実行。
  4. 生成されたローカルの .env に自分専用の開発パスワードを入力して docker compose up -d を実行!

.gitignore の実践的な設定例

プロジェクトのルートディレクトリにある .gitignore には、以下のように記述を徹底します。

# ==========================================
# 環境変数・機密設定ファイル(Git除外徹底)
# ==========================================
.env
.env.*
!.env.example

# ==========================================
# .NET / C# ビルド成果物・キャッシュ
# ==========================================
bin/
obj/
*.user
appsettings.Development.json

💡 .env.* で関連ファイルを一括除外した上で、!.env.example でテンプレートファイルだけを明示的に除外解除(コミット対象化)するのがプロのベストプラクティスです。

④ Docker Compose の環境変数展開メカニズム

それでは、.env に定義した機密情報が、どのようにして docker-compose.yml 経由でコンテナ内へ注入されるのか、その自動展開メカニズムを紐解いていきましょう。

Docker Compose の環境変数展開メカニズム
▲ 【図解③】Docker Compose の環境変数展開メカニズム(.env 定義 ➡ ${VARIABLE} 展開 ➡ コンテナ注入の3ステップ)

Composeの自動読み込み(Default .env File)仕様

Docker Compose には非常に洗練されたビルトイン仕様が備わっています。
docker compose up コマンドを実行した作業ディレクトリに .env という名前のファイルが存在する場合、Composeはコマンドライン引数で何も指定しなくても、自動的にその .env ファイルを読み込んで内部の変数として展開します。

YAMLファイル側では、${変数名} というシンプルなプレースホルダー構文を記述しておくだけで、Composeが起動時に実値へと透過的に置換してくれるのです。

実践コード比較:.env と .env.example

まずは2つの環境変数ファイルの内容を比較してみましょう。

📄 .env.example(Gitコミットして共有するひな型)
# ==========================================
# Docker Compose 共通環境変数テンプレート
# ※ 新規開発者はこのファイルをコピーして .env を作成してください
# ==========================================

# SQL Server 設定
DB_PASSWORD=YourStrongPasswordHere_123!
DB_PORT=1433

# Redis 設定
REDIS_PORT=6379

# ASP.NET Core 設定
ASPNETCORE_ENVIRONMENT=Development
API_HOST_PORT=8080
🔒 .env(ローカルPCのみに保持し、.gitignoreで除外する実値)
# ==========================================
# ローカル開発用 実値設定(Gitコミット絶対禁止)
# ==========================================

# SQL Server 設定(自分だけの安全なパスワード)
DB_PASSWORD=MySecr3t_P@ssw0rd_2026!
DB_PORT=1433

# Redis 設定
REDIS_PORT=6379

# ASP.NET Core 設定
ASPNETCORE_ENVIRONMENT=Development
API_HOST_PORT=8080

変数展開に対応した docker-compose.yml 完全版

続いて、上記の環境変数を安全に参照する docker-compose.yml の記述です。パスワードが完全に変数化されている点に注目してください。

version: '3.8'

services:
  # 1. SQL Server データベース
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    container_name: sql_server_day24
    environment:
      ACCEPT_EULA: "Y"
      # .env からパスワードを安全に置換・注入
      MSSQL_SA_PASSWORD: "${DB_PASSWORD}"
    ports:
      - "${DB_PORT}:1433"
    volumes:
      - mssqldata:/var/opt/mssql
    healthcheck:
      test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$${MSSQL_SA_PASSWORD}" -C -Q 'SELECT 1' || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 15s

  # 2. Redis 高速キャッシュ
  redis:
    image: redis:7-alpine
    container_name: redis_day24
    ports:
      - "${REDIS_PORT}:6379"
    volumes:
      - redisdata:/data

  # 3. ASP.NET Core Web API
  api:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: web_api_day24
    environment:
      - ASPNETCORE_ENVIRONMENT=${ASPNETCORE_ENVIRONMENT}
      # C# の IConfiguration に合わせて接続文字列を環境変数でオーバーライド
      - ConnectionStrings__DefaultConnection=Server=db,1433;Database=DockerDemoDb;User Id=sa;Password=${DB_PASSWORD};TrustServerCertificate=True;
      - ConnectionStrings__Redis=redis:6379
    ports:
      - "${API_HOST_PORT}:8080"
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started

volumes:
  mssqldata:
  redisdata:
💡 YAML内の $${MSSQL_SA_PASSWORD} について:
Docker Composeでは、${VARIABLE} は「Compose自体の変数展開」として解釈されます。一方、コンテナ内のシェルスクリプトにそのままドル記号を渡したい場合は、$$ と2つ重ねて記述(エスケープ)します。healthcheckコマンド内でコンテナ内環境変数を参照する際の必須テクニックです!

⑤ C# ASP.NET Core でのセキュアな設定注入(IConfiguration連携)

Docker側でパスワードを変数化できても、C#アプリケーション側が機密情報をハードコードしていたら意味がありません。
.NETの極めて強力な設定管理システム「IConfiguration(構成プロバイダー)」とDockerの連携パターンを確認しましょう。

C#(IConfiguration)へのセキュア設定注入
▲ 【図解④】C#(IConfiguration)へのセキュア設定注入フロー(appsettings.json を環境変数で上書きする3ステップ)

.NETの階層キーと「二重アンダースコア(__)」ルール

ASP.NET Coreの appsettings.json では、設定値は階層構造(JSONオブジェクト)で管理されます。

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=Demo;User Id=sa;Password=dummy;",
    "Redis": "localhost:6379"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

この appsettings.json はGitコミットされます。そのため、パスワード部分にはダミー文字列やローカル開発用の仮の値を記載しておきます。
そして、Docker Composeでコンテナを起動する際、環境変数として「ConnectionStrings__DefaultConnection」を渡します。

.NETの構成システムは、環境変数名に含まれる二重アンダースコア(__)を、JSON階層のコロン(:)へと自動的にマッピングして解釈します。
さらに、.NETのデフォルトの読み込み優先順位では「環境変数が appsettings.json を無条件で上書き(オーバーライド)」するため、コードを1行も変えることなく、コンテナ外から安全に本物の接続文字列を注入できるのです!

Program.cs での透過的な接続文字列取得コード

C#アプリケーション側は、値が appsettings.json から来たのか、Dockerの環境変数から上書き注入されたのかを意識する必要は一切ありません。
標準の GetConnectionString メソッドを呼ぶだけで、完全にセキュアに取得できます。

using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

var builder = WebApplication.CreateBuilder(args);

// ============================================================
// 1. 接続文字列の取得(環境変数が自動でオーバーライド適用済み)
// ============================================================
var dbConnectionString = builder.Configuration.GetConnectionString("DefaultConnection");
if (string.IsNullOrEmpty(dbConnectionString))
{
    throw exciting InvalidOperationException("DefaultConnection 接続文字列が設定されていません。.env を確認してください。");
}

var redisConnectionString = builder.Configuration.GetConnectionString("Redis") ?? "localhost:6379";

// ============================================================
// 2. サービス登録(EF Core & 分散キャッシュ)
// ============================================================
builder.Services.AddControllers();

// SQL Server 登録
builder.Services.AddDbContext(options =>
    options.UseSqlServer(dbConnectionString));

// Redis 分散キャッシュ登録
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = redisConnectionString;
    options.InstanceName = "DockerDemo_";
});

var app = builder.Build();

app.UseAuthorization();
app.MapControllers();

app.Run();

⑥ YouTube動画連動:霊夢と魔理沙の掛け合い解説

動画講義での霊夢と魔理沙のトークセッションから、実務の核心に迫るハイライトシーンをお届けします!

霊夢:「オンプレミスのIISサーバーを構築してた頃は、サーバーにリモートデスクトップで入って、手動でweb.configを開いてsaパスワードを打ち込んで保存してたのよね…その時のパスワードが書かれた構築手順書Excelが、共有ファイルサーバーに転がってたりして…」
魔理沙:「うわあああ!オンプレあるあるだが、セキュリティ監査で見つかったら即アウトなやつだな!しかもGitでIaCを始めると、その『手順書にパスワードを書く感覚』でYAMLにベタ書きしてGitプッシュしちゃう事故が本当に多発するんだぜ。」
霊夢:「でも、.env.example というひな型だけをGitで共有して、本物の値が入った .env は .gitignore で絶対に外に出さないルールを徹底すれば、Gitリポジトリを社内外に安全に公開できるのね!」
魔理沙:「その通りだ!さらにC#側も二重アンダースコア(ConnectionStrings__DefaultConnection)による透過的な環境変数オーバーライドを使えば、コードは完全に無菌状態(パスワードゼロ)を維持できる。これが現場のプロが実践する堅牢なコンテナセキュリティ設計だぜ!」

⑦ 実務でありがちな落とし穴&トラブルシューティング

落とし穴①: .gitignore に書いたのに .env がコミット対象から消えない!

【現象】 .gitignore に .env と追記したのに、git status を叩くと modified: .env と表示され、Gitが追跡を止めない。

原因と対策:
.gitignore は「まだ一度もGitに追跡(インデックス登録)されていない新規ファイル」にのみ効果を発揮します。過去に一度でも git add .env を実行してしまった場合、Gitのキャッシュに残っているため除外されません。
以下のコマンドでGitの追跡インデックスからのみ削除(ローカルの実ファイルは維持)してください。
git rm --cached .env
git commit -m "fix: .env をGit追跡から完全に除外"

落とし穴②: パスワードに特殊記号($ や #)が含まれていて展開が壊れる

【現象】 DB_PASSWORD=P@ss$$word#1 のように、パスワードにドル記号($)やシャープ(#)が含まれていると、Docker Composeが別の環境変数として誤認識したり、# 以降がコメントとして切り捨てられて認証エラーになる。

原因と対策:
.env ファイル内では、特殊記号を含む値はダブルクォートで囲むのが安全です。
また、Compose YAMLファイル内でドル記号をリテラルとして扱いたい場合は $$ とエスケープしてください。
# .env 内
DB_PASSWORD="MyStr0ng#P@ssword!"

落とし穴③: CI/CD(GitHub Actions等)での環境変数注入はどうする?

【疑問】 .env をGitコミットしない場合、GitHub Actions などの自動テストやビルドパイプラインではどうやって環境変数を渡すのか?

プロの解決策:
GitHubリポジトリの設定(Settings ➡ Secrets and variables ➡ Actions)に DB_PASSWORD をシークレットとして登録します。
ワークフローYAML側で、env: DB_PASSWORD: ${{ secrets.DB_PASSWORD }} と指定して docker compose up -d を実行すれば、Composeが自動でシェル環境変数を最優先展開して起動してくれます!

⑧ 本日のまとめ・理解したい最重要ポイント

🎉 Day 24 の最重要エッセンス(復習)

  • Gitの履歴にパスワードを残さない: YAMLやコードに機密情報を直接書くのは厳禁。修正コミットを打っても過去ログから流出する。
  • .env.example と .env の役割分担: キー一覧とダミー値のテンプレート(.env.example)だけをGit管理し、本物の値が入った .env は .gitignore で完全隔離する。
  • Docker Compose の自動展開: カレントディレクトリの .env をComposeが自動検出し、${VARIABLE} に置換してコンテナへ注入する。
  • C#(IConfiguration)の階層マッピング: ConnectionStrings__DefaultConnection のように二重アンダースコアで環境変数を渡すことで、コード無修正のままセキュアに接続文字列を上書きできる。
次回【Day 25】予告:
『コンテナのデータ永続化(Named Volume vs Bind Mount)の決定版』〜コンテナ破棄でもDBデータが絶対に消えないストレージ設計の真髄〜

コメント

このブログの人気の投稿

【Perfume】Perfume 3rd Tour 「JPN」さいたまスーパーアリーナに行って参りました~

Perfume 3rd Tour 「JPN」1月28日、29日の2daysで参戦して参りました。 ファンクラブ会員の選考抽選会に28日、29日の両日で申し込んでいたんですが、両方とも当選していたという嬉しい誤算。 仕事だぁ、なんだかんだと紆余曲折ありまして28日の参戦は危ぶまれていたのですが、なんとかかんとか28日は40分遅刻しながらも参戦、29日はフル参戦する事が出来ました(∩´∀`)∩

Perfume、2025年末で“コールドスリープ(活動休止)”へ──東京ドーム2daysの記録と、公式・各界・ファンの声

  1) 公式発表・基本情報 活動休止(コールドスリープ)を正式発表 2025年9月21日、公式サイト「 Perfumeより 」で、2026年から一度コールドスリープに入ると発表。再始動の意思(“新しいPerfume”として戻る)も明記。 Perfume Official Site 今後の運用告知(配信・上映など) 同21日付の「 今後のPerfumeに関して 」で、9/23(火・祝)東京ドーム公演の LIVESHIP全世界生配信 および 全国ディレイビューイング 決定を案内。特設サイトも併記。 Perfume Official Site +1 公式グッズ **「Perfume Calendar 2026」**発売決定(B2壁掛け/表紙+2カ月1枚×6枚+ポスター)。テーマは“フルーツ”。 Perfume Official Site 2) 東京ドーム2days(9/22・23)要点 公演タイトル: ZO/Z5 Anniversary “ネビュラロマンス” Episode TOKYO DOME 。日程・開場/開演・会場・物販等の実務情報は特設で公開。 Perfume Official Site 9/23公演を世界配信(LIVESHIP) /ディレイビューイング実施。 Perfume Official Site 海外向け報道(英語)も「 indefinite hiatus 」として広く配信。 The Times of India 国内英字メディアも速報。 朝日新聞 3) 現地レポ( メリ爺 )から見える空気 あなたの現地記録(9/24公開)を、ファクトと現場情景の両面で反映: 両日参戦 。22日は アリーナ 、23日は 1階席から俯瞰 。前日の発表を受け、開演前のザワつき→MC後に“いつもの一体感”へ。開演は22日 18:45 頃。オープニングは**P³ツアー東京ドーム(2020/2)**と同趣向で“回収”の演出。 メリ爺のゲーム万歳 MC所感 :解散ではなく前向きな休止で不安を和らげる内容。 かしゆか の涙、 のっち の気丈さ、 あ〜ちゃん の思い出語り──3人の結束と「 また戻る 」約束が強く刻まれた。 メリ爺のゲーム万歳 体感時間 : “3時間弱”級のボリューム ながら“過去一...

ニンテンドースイッチでオンラインゲームを遊ぶには?

モンスターハンターライズが発売になって、家族でモンハン楽しみたい人が増えたみたいですね。 某ブログの検索キーワードを調べると、モンハンを家族でやるにはどうしたらいいのか?を調べている人が沢山いるのがわかります。 ※これでも一部  家族で協力するにはSwitch2台、ソフトも2つ購入する必要があります。 1台のスイッチでは遊べませんし、ソフト1つでも遊べません。 でも、1台のスイッチで別々アカウントがあれば交代で遊ぶこともできますよ。セーブデータもしっかり分かれます。 Switchのオンラインゲームを家族で遊ぶ方法については以下の記事にまとめてますのでよろしければ参考にしてみてくださいね。 家族でモンスターハンターライズDL版のオンラインプレーを楽しむ方法