EcAuth シークレット管理ガイド
1Password を中心としたシークレット管理運用。ローカル開発・CI・Terraform・Azure を横断する設計。
概要
EcAuth プロジェクトでは、環境変数とシークレットをハイブリッド構成で管理します。
- ローカル開発環境: 1Password CLI
(
op inject) - GitHub Actions: 1Password Service Account + GitHub Secrets
- Azure 環境: Azure Key Vault(Terraform 経由で 1Password から同期)
アーキテクチャ
EcAuth
*-dev-*
開発環境用
*-staging-*
ステージング用
*-prod-*
本番用
op inject でテンプレから .env を生成
1password/load-secrets-action でジョブ環境に注入
- Storage tfstate 永続化
- Key Vault 本番シークレット参照
- App Service ランタイム環境変数
Service Account 構成
3つの Service Account トークンを使い分けています。
| GitHub Secret 名 | 用途 | アクセス可能アイテム |
|---|---|---|
OP_SERVICE_ACCOUNT_TOKEN |
インフラ用(Terraform) | ecauth-*, mockidp-*,
skirnir-workload-identity/* |
OP_SERVICE_ACCOUNT_TOKEN_NONPROD |
NonProd アプリ用 | ecauth-dev-*, ecauth-staging-*,
mockidp-* |
OP_SERVICE_ACCOUNT_TOKEN_PROD |
本番アプリ用 | ecauth-prod-* |
GitHub Secrets 設定
# インフラ用トークン(Terraform ワークフロー)
gh secret set OP_SERVICE_ACCOUNT_TOKEN \
--org EcAuth \
--visibility all \
--body "<インフラ用トークン>"
# NonProd 用トークン(staging, MockIdP staging)
gh secret set OP_SERVICE_ACCOUNT_TOKEN_NONPROD \
--org EcAuth \
--visibility all \
--body "<NonProd 用トークン>"
# Prod 用トークン(MockIdP production)
gh secret set OP_SERVICE_ACCOUNT_TOKEN_PROD \
--org EcAuth \
--visibility all \
--body "<Prod 用トークン>"
# その他
gh secret set ORG_PAT --org EcAuth --visibility all --body "<GitHub PAT>"
gh secret set MOCK_IDP_BASE_URL --org EcAuth --visibility all --body "<MockIdP URL>"1Password 保管庫構成
保管庫
| 保管庫名 | 用途 | アクセス権限 |
|---|---|---|
| EcAuth | EcAuth プロジェクト全体 | 開発チーム全員 |
| skirnir-workload-identity | Azure 共通認証情報(Terraform のみ) | インフラ担当のみ |
アイテム命名規則
{プロジェクト}-{環境}-{サービス種別}
例:
ecauth-dev-sql → EcAuth / 開発環境 / SQL Database
ecauth-prod-app → EcAuth / 本番環境 / アプリ設定
mockidp-staging → MockIdP / ステージング / 設定
アイテム構成
保管庫: EcAuth
│
├── 【データベース】
│ ├── ecauth-dev-sql ← MockIdP 用 (Azure SQL Database)
│ │ ├── hostname: mock-openid-provider-xxx.database.windows.net
│ │ ├── database: MockIdpDb
│ │ ├── username: ****
│ │ └── password: ****
│ │
│ ├── ecauth-staging-sql ← EcAuth IdentityProvider 用 (staging)
│ │ ├── hostname: ecauth-staging-xxx.database.windows.net
│ │ ├── database: EcAuthDb
│ │ ├── username: ****
│ │ └── password: ****
│ │
│ └── ecauth-prod-sql ← EcAuth IdentityProvider 用 (production)
│ └── (同様)
│
├── 【アプリケーション】
│ ├── ecauth-dev-app
│ │ ├── state_password: ****
│ │ ├── default_client_id: client_id
│ │ └── default_client_secret: ****
│ │
│ ├── ecauth-staging-app ← セクション内に追加フィールドあり
│ │ ├── state_password: ****
│ │ ├── client_id: ****
│ │ ├── client_secret: ****
│ │ ├── organization_code: ****
│ │ ├── organization_name: ****
│ │ ├── organization_tenant_name: ****
│ │ ├── redirect_uri: ****
│ │ ├── app_name: ****
│ │ ├── web_app_suffix: ****
│ │ ├── b2b_user_subject: ****
│ │ ├── b2b_user_external_id: ****
│ │ ├── b2b_redirect_uri: ****
│ │ └── b2b_allowed_rp_ids: ****
│ │
│ └── ecauth-prod-app
│ ├── state_password: ****
│ ├── client_id: ****
│ ├── client_secret: ****
│ ├── organization_code: ****
│ ├── organization_name: ****
│ ├── organization_tenant_name: ****
│ ├── redirect_uri: ****
│ ├── app_name: ****
│ ├── web_app_suffix: ****
│ ├── b2b_user_subject: ****
│ ├── b2b_user_external_id: ****
│ ├── b2b_redirect_uri: ****
│ └── b2b_allowed_rp_ids: ****
│
├── 【MockIdP】
│ ├── mockidp-dev ← 開発環境用
│ │ ├── base_url: https://mock-openid-provider.xxx.azurecontainerapps.io
│ │ ├── authorization_endpoint: https://mock-openid-provider.xxx.azurecontainerapps.io/authorization
│ │ ├── token_endpoint: https://mock-openid-provider.xxx.azurecontainerapps.io/token
│ │ ├── userinfo_endpoint: https://mock-openid-provider.xxx.azurecontainerapps.io/userinfo
│ │ ├── default_client_id: mockclientid
│ │ ├── default_client_secret: ****
│ │ ├── default_user_email: defaultuser@example.com
│ │ ├── default_user_password: ****
│ │ ├── federate_client_id: federateclientid
│ │ └── federate_client_secret: ****
│ │
│ ├── mockidp-staging ← ステージング用
│ │ ├── base_url: ****
│ │ ├── authorization_endpoint: ****
│ │ ├── token_endpoint: ****
│ │ ├── userinfo_endpoint: ****
│ │ ├── default_client_id: ****
│ │ ├── default_client_secret: ****
│ │ ├── default_user_email: ****
│ │ ├── default_user_password: ****
│ │ └── redirect_uri: ****
│ │
│ └── mockidp-production ← 本番用
│ ├── base_url: ****
│ ├── authorization_endpoint: ****
│ ├── token_endpoint: ****
│ ├── userinfo_endpoint: ****
│ ├── default_client_id: ****
│ ├── default_client_secret: ****
│ ├── default_user_email: ****
│ └── default_user_password: ****
│
├── 【MockIdP 連携】
│ ├── ecauth-staging-mockidp ← EcAuth → MockIdP 連携設定(ステージング)
│ │ ├── app_name: ****
│ │ ├── client_id: ****
│ │ └── client_secret: ****
│ │
│ └── ecauth-prod-mockidp ← EcAuth → MockIdP 連携設定(本番)
│ ├── app_name: ****
│ ├── client_id: ****
│ └── client_secret: ****
│
├── 【Workload Identity】
│ └── ecauth-workload-identity ← Azure Workload Identity 認証情報
│ ├── AZURE_CLIENT_ID: ****
│ ├── AZURE_TENANT_ID: ****
│ └── AZURE_SUBSCRIPTION_ID: ****
│
├── 【インフラストラクチャ】
│ └── ecauth-infrastructure-sp ← Azure Service Principal
│ └── AZURE_CLIENT_ID: ****
│
└── 【外部 IdP】(オプション)
├── google-oauth-dev
│ ├── client_id: ****
│ └── client_secret: ****
│
└── amazon-oauth-dev
├── client_id: ****
└── client_secret: ****
保管庫: skirnir-workload-identity
│
└── azure-common-credentials ← Azure 共通認証情報
├── AZURE_TENANT_ID: ****
└── AZURE_SUBSCRIPTION_ID: ****
※ EcAuth ワークフロー(staging.yml, production.yml)では ecauth-workload-identity を使用。
skirnir-workload-identity は Terraform ワークフロー(ecauth-infrastructure)のみで使用。
ローカル開発環境
1. 1Password CLI のインストール
# macOS
brew install 1password-cli
# Linux (Debian/Ubuntu)
curl -sS https://downloads.1password.com/linux/keys/1password.asc | \
sudo gpg --dearmor --output /usr/share/keyrings/1password-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/1password-archive-keyring.gpg] https://downloads.1password.com/linux/debian/$(dpkg --print-architecture) stable main" | \
sudo tee /etc/apt/sources.list.d/1password.list
sudo apt update && sudo apt install 1password-cli
# Windows
winget install AgileBits.1Password.CLI2. サインイン
# 初回サインイン
eval $(op signin)
# 生体認証を有効化している場合は自動的に認証3. .env ファイルの生成
cd EcAuth
# 開発環境用の .env を生成
op inject -i .env.dev.tpl -o .env
# Docker Compose と組み合わせて使用
op run --env-file=.env.dev.tpl -- docker compose up -d4. .env.tpl テンプレートファイル
開発環境用
(EcAuth/.env.dev.tpl)
EcAuth DB は Docker 内、MockIdP は Azure dev 環境を使用。
# =============================================================================
# Database Configuration (Docker 内)
# =============================================================================
DB_HOST=db
MIGRATION_DB_HOST=localhost
DB_NAME=EcAuthDb
DB_USER=SA
DB_PASSWORD='<YourStrong@Passw0rd>'
STATE_PASSWORD='<strong_password_string_of_at_least_32_characters>'
# =============================================================================
# Default Organization/Client Settings
# =============================================================================
DEFAULT_ORGANIZATION_CODE=example
DEFAULT_ORGANIZATION_NAME=example
DEFAULT_ORGANIZATION_TENANT_NAME=example
DEFAULT_ORGANIZATION_REDIRECT_URI=https://localhost:8081/v1/auth/callback
DEFAULT_CLIENT_ID=client_id
DEFAULT_CLIENT_SECRET=client_secret
DEFAULT_APP_NAME=app_name
# =============================================================================
# Azure MockIdP (dev organization) Settings
# =============================================================================
MOCK_IDP_BASE_URL=op://EcAuth/mockidp-dev/base_url
# dev organization の MockIdP クライアント設定
MOCK_IDP_DEFAULT_CLIENT_ID=op://EcAuth/mockidp-dev/default_client_id
MOCK_IDP_DEFAULT_CLIENT_SECRET=op://EcAuth/mockidp-dev/default_client_secret
MOCK_IDP_DEFAULT_CLIENT_NAME=MockClient
MOCK_IDP_DEFAULT_USER_EMAIL=op://EcAuth/mockidp-dev/default_user_email
MOCK_IDP_DEFAULT_USER_PASSWORD=op://EcAuth/mockidp-dev/default_user_password
# Federate クライアント設定(EcAuth -> Azure MockIdP 連携用)
MOCK_IDP_FEDERATE_CLIENT_ID=op://EcAuth/mockidp-dev/federate_client_id
MOCK_IDP_FEDERATE_CLIENT_SECRET=op://EcAuth/mockidp-dev/federate_client_secret
MOCK_IDP_FEDERATE_CLIENT_NAME=FederateClient
MOCK_IDP_FEDERATE_USER_EMAIL=federate@example.jp
# =============================================================================
# Federate OAuth2 Settings (EcAuth -> Azure MockIdP)
# =============================================================================
FEDERATE_OAUTH2_APP_NAME=federate-oauth2
FEDERATE_OAUTH2_CLIENT_ID=op://EcAuth/mockidp-dev/federate_client_id
FEDERATE_OAUTH2_CLIENT_SECRET=op://EcAuth/mockidp-dev/federate_client_secret
FEDERATE_OAUTH2_AUTHORIZATION_ENDPOINT=op://EcAuth/mockidp-dev/authorization_endpoint
FEDERATE_OAUTH2_TOKEN_ENDPOINT=op://EcAuth/mockidp-dev/token_endpoint
FEDERATE_OAUTH2_USERINFO_ENDPOINT=op://EcAuth/mockidp-dev/userinfo_endpoint
# =============================================================================
# Google OAuth2 Settings (オプション)
# =============================================================================
GOOGLE_OAUTH2_APP_NAME=google-oauth2
GOOGLE_OAUTH2_CLIENT_ID=<Google OAuth2 client_id>
GOOGLE_OAUTH2_CLIENT_SECRET=<Google OAuth2 client_secret>
GOOGLE_OAUTH2_DISCOVERY_URL=https://accounts.google.com/.well-known/openid-configurationステージング環境用
(EcAuth/.env.staging.tpl)
EcAuth DB と MockIdP の両方が Azure 環境。
# =============================================================================
# Database Configuration (Azure SQL Database)
# =============================================================================
SQL_HOST=op://EcAuth/ecauth-staging-sql/hostname
SQL_DATABASE=op://EcAuth/ecauth-staging-sql/database
SQL_USERNAME=op://EcAuth/ecauth-staging-sql/username
SQL_PASSWORD=op://EcAuth/ecauth-staging-sql/password
# =============================================================================
# Staging Organization/Client Settings
# =============================================================================
STAGING_ORGANIZATION_CODE=op://EcAuth/ecauth-staging-app/organization_code
STAGING_ORGANIZATION_NAME=op://EcAuth/ecauth-staging-app/organization_name
STAGING_ORGANIZATION_TENANT_NAME=op://EcAuth/ecauth-staging-app/organization_tenant_name
STAGING_CLIENT_ID=op://EcAuth/ecauth-staging-app/client_id
STAGING_CLIENT_SECRET=op://EcAuth/ecauth-staging-app/client_secret
STAGING_APP_NAME=op://EcAuth/ecauth-staging-app/app_name
STAGING_REDIRECT_URI=op://EcAuth/ecauth-staging-app/redirect_uri
# =============================================================================
# Staging MockIdP Settings (Azure MockIdP との連携)
# =============================================================================
STAGING_MOCK_IDP_APP_NAME=op://EcAuth/ecauth-staging-mockidp/app_name
STAGING_MOCK_IDP_CLIENT_ID=op://EcAuth/ecauth-staging-mockidp/client_id
STAGING_MOCK_IDP_CLIENT_SECRET=op://EcAuth/ecauth-staging-mockidp/client_secret
# MockIdP エンドポイント(Azure Container Apps)
MOCK_IDP_BASE_URL=op://EcAuth/mockidp-staging/base_url
STAGING_MOCK_IDP_AUTHORIZATION_ENDPOINT=op://EcAuth/mockidp-staging/authorization_endpoint
STAGING_MOCK_IDP_TOKEN_ENDPOINT=op://EcAuth/mockidp-staging/token_endpoint
STAGING_MOCK_IDP_USERINFO_ENDPOINT=op://EcAuth/mockidp-staging/userinfo_endpointGitHub Actions
シークレット管理方針
| シークレット | 保存先 | 理由 |
|---|---|---|
ORG_PAT |
GitHub Secrets | GitHub → GitHub 認証は GitHub 内で完結 |
OP_SERVICE_ACCOUNT_TOKEN* |
GitHub Secrets | 1Password 連携に必須 |
MOCK_IDP_BASE_URL |
GitHub Secrets | playwright.yml で使用(URL 情報) |
| その他すべて | 1Password | 一元管理のため |
現在のワークフロー構成
| ワークフロー | リポジトリ | 1Password トークン | Azure 認証 | 備考 |
|---|---|---|---|---|
| dotnet_tests.yml | EcAuth | なし | なし | ローカルテストのみ |
| playwright.yml | EcAuth | なし | なし | MOCK_IDP_BASE_URL は GitHub Secrets |
| staging.yml | EcAuth | _NONPROD |
1Password 経由 | migrate + build + deploy + verify |
| production.yml | EcAuth | _PROD |
1Password 経由 | migrate + build + deploy + verify(手動実行のみ) |
| terraform.yml | ecauth-infrastructure | OP_SERVICE_ACCOUNT_TOKEN |
1Password 経由 | Azure 認証情報も 1Password |
| deploy.yml | EcAuth.MockIdP | なし | GitHub Secrets | Workload Identity |
| migrate.yml | EcAuth.MockIdP | _NONPROD / _PROD |
なし | 環境別トークン |
ワークフロー例
基本的なワークフロー(1Password + Workload Identity)
# .github/workflows/staging.yml(deploy ジョブ抜粋)
name: Staging Deploy & Migrate
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: staging
permissions:
id-token: write
contents: read
steps:
- name: Load secrets from 1Password
uses: 1password/load-secrets-action@v3
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_NONPROD }}
AZURE_CLIENT_ID: op://EcAuth/ecauth-workload-identity/AZURE_CLIENT_ID
AZURE_TENANT_ID: op://EcAuth/ecauth-workload-identity/AZURE_TENANT_ID
AZURE_SUBSCRIPTION_ID: op://EcAuth/ecauth-workload-identity/AZURE_SUBSCRIPTION_ID
WEB_APP_SUFFIX: op://EcAuth/ecauth-staging-app/web_app_suffix
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- name: Deploy to Azure Web App
uses: azure/webapps-deploy@v3
with:
app-name: ecauth-staging-${{ env.WEB_APP_SUFFIX }}
package: ./publishTerraform ワークフロー(1Password から Azure 認証情報を取得)
# ecauth-infrastructure/.github/workflows/terraform.yml
name: Terraform
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
plan-staging:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Load secrets from 1Password
uses: 1password/load-secrets-action@v2
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
AZURE_TENANT_ID: op://skirnir-workload-identity/azure-common-credentials/AZURE_TENANT_ID
AZURE_SUBSCRIPTION_ID: op://skirnir-workload-identity/azure-common-credentials/AZURE_SUBSCRIPTION_ID
AZURE_CLIENT_ID: op://EcAuth/ecauth-infrastructure-sp/AZURE_CLIENT_ID
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: "~1.7"
- name: Terraform Init
working-directory: environments/staging
run: terraform init
env:
ARM_USE_OIDC: true
ARM_CLIENT_ID: ${{ env.AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ env.AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ env.AZURE_SUBSCRIPTION_ID }}
- name: Terraform Plan
working-directory: environments/staging
run: terraform plan -no-color
env:
ARM_USE_OIDC: true
ARM_CLIENT_ID: ${{ env.AZURE_CLIENT_ID }}
ARM_TENANT_ID: ${{ env.AZURE_TENANT_ID }}
ARM_SUBSCRIPTION_ID: ${{ env.AZURE_SUBSCRIPTION_ID }}
TF_VAR_op_service_account_token: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}Terraform 連携
Azure Storage Backend
Terraform の状態管理は Azure Storage Backend を使用します。
Backend 設定
environments/staging/terraform.tf:
terraform {
required_version = ">= 1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
azuread = {
source = "hashicorp/azuread"
version = "~> 3.0"
}
onepassword = {
source = "1Password/onepassword"
version = "~> 3.0"
}
}
# Azure Storage Backend
backend "azurerm" {
resource_group_name = "ec-auth"
storage_account_name = "stateecauthinfra"
container_name = "tfstate"
key = "staging.tfstate"
use_oidc = true
use_azuread_auth = true
}
}
provider "azurerm" {
features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
use_oidc = true
}
provider "azuread" {
use_oidc = true
}
provider "onepassword" {
service_account_token = var.op_service_account_token
}
1Password Terraform Provider
Terraform から 1Password のシークレットを直接参照できます。
Data Source でシークレット取得
environments/staging/main.tf:
# 1Password からシークレット取得
data "onepassword_item" "staging_sql" {
# vault は UUID で指定する必要がある(v3.0 要件)
# UUID は `op vault get "EcAuth" --format json | jq -r '.id'` で取得
vault = "vdddxjhqbqi4rbapcr656fa5vu"
title = "ecauth-staging-sql"
}
data "onepassword_item" "staging_app" {
vault = "vdddxjhqbqi4rbapcr656fa5vu"
title = "ecauth-staging-app"
}
locals {
# v3.0 では DATABASE カテゴリの標準属性から直接取得可能
sql_username = data.onepassword_item.staging_sql.username
sql_password = data.onepassword_item.staging_sql.password
sql_host = data.onepassword_item.staging_sql.hostname
sql_database = data.onepassword_item.staging_sql.database
# セクション内のカスタムフィールドから取得
web_app_suffix = [for f in data.onepassword_item.staging_app.section[0].field : f.value if f.label == "web_app_suffix"][0]
state_password = [for f in data.onepassword_item.staging_app.section[0].field : f.value if f.label == "state_password"][0]
organization_tenant_name = [for f in data.onepassword_item.staging_app.section[0].field : f.value if f.label == "organization_tenant_name"][0]
redirect_uri = [for f in data.onepassword_item.staging_app.section[0].field : f.value if f.label == "redirect_uri"][0]
sql_connection_string = "Server=tcp:${local.sql_host},1433;Initial Catalog=${local.sql_database};Persist Security Info=False;User ID=${local.sql_username};Password=${local.sql_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
}
v3.0 では 1Password SDK を使用するため、GitHub Actions 環境でも op CLI のインストールは不要です。
Key Vault にシークレット保存
module "key_vault" {
source = "../../modules/key-vault"
# ... 既存の設定 ...
# 1Password から取得した SQL 接続文字列と state-password を追加
secret_names = concat(var.key_vault_secret_names, ["sql-connection-string", "state-password"])
secret_values = merge(var.key_vault_secret_values, {
"sql-connection-string" = local.sql_connection_string
"state-password" = local.state_password
})
}
App Service から Key Vault 参照
module "web_app_identityprovider" {
source = "../../modules/web-app"
# Key Vault 参照(Azure Portal で平文表示されない)
connection_string_name = "EcAuthDbContext"
connection_string = "@Microsoft.KeyVault(VaultName=${var.key_vault_name};SecretName=sql-connection-string)"
app_settings = {
"STATE_PASSWORD" = "@Microsoft.KeyVault(VaultName=${var.key_vault_name};SecretName=state-password)"
}
}
データフロー
┌─────────────────────────────────────────────────────────────┐
│ 1Password (単一の情報源) │
│ ecauth-staging-sql │
└─────────────────────────────────────────────────────────────┘
│
│ 1Password Terraform Provider (terraform apply 時)
▼
┌─────────────────────────────────────────────────────────────┐
│ GitHub Actions + Terraform │
│ (シークレットはログでマスク) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Azure Key Vault │
│ sql-connection-string │
└─────────────────────────────────────────────────────────────┘
│
│ @Microsoft.KeyVault(...) 参照
▼
┌─────────────────────────────────────────────────────────────┐
│ Azure App Service │
│ (起動時にキャッシュ、Portal で非表示) │
└─────────────────────────────────────────────────────────────┘
Azure Key Vault(本番環境)
Key Vault 参照
App Service から Key Vault を参照する場合、起動時にキャッシュされるため、毎リクエストで Key Vault にアクセスしません。
| タイミング | Key Vault アクセス |
|---|---|
| App Service 起動/再起動 | あり |
| 設定変更時 | あり |
| 定期リフレッシュ | 24時間ごと |
| 各 HTTP リクエスト | なし(キャッシュ使用) |
// Program.cs
if (builder.Environment.IsProduction())
{
var keyVaultName = builder.Configuration["KeyVaultName"];
var keyVaultUri = new Uri($"https://{keyVaultName}.vault.azure.net/");
builder.Configuration.AddAzureKeyVault(
keyVaultUri,
new DefaultAzureCredential());
}セキュリティベストプラクティス
1. アクセス権限の最小化
- 開発者には開発環境のシークレットのみアクセス権限を付与
- 本番環境のシークレットは限定されたメンバーのみ
2. シークレットのローテーション
# 定期的なパスワード更新
# 1. 1Password でシークレットを更新
# 2. Terraform apply を実行して Key Vault に同期
# 3. Azure App Service を再起動3. 監査ログ
- 1Password: アクティビティログで誰がいつアクセスしたか確認可能
- Azure Key Vault: 診断ログを有効化してアクセス監査
4. .env ファイルの取り扱い
# .gitignore
.env
.env.local
.env.*.local
# テンプレートファイルはコミット可
!.env.*.tpl
トラブルシューティング
1Password CLI の認証エラー
# セッションの再認証
eval $(op signin)
# アカウント情報の確認
op account listシークレット参照エラー
# シークレットの存在確認
op item get "ecauth-dev-sql" --vault EcAuth
# フィールド一覧の確認
op item get "ecauth-dev-sql" --vault EcAuth --format json | jq '.fields[].label'GitHub Actions での 1Password エラー
# デバッグ用: シークレットの読み込み確認
- name: Verify 1Password connection
uses: 1password/load-secrets-action@v2
with:
export-env: false # 環境変数にエクスポートしない
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
TEST_SECRET: op://EcAuth/ecauth-dev-sql/databaseApp Service の Key Vault 参照エラー(SecretNotFound)
Azure App Service で Key Vault
参照を使用している場合、SecretNotFound
エラーが発生することがあります。
症状:
コンテナー名: ecauth-staging-kv
シークレットの名前: sql-connection-string
SecretNotFound
Key Vault reference was not able to be resolved because Key Vault reference contains invalid Key Vault secret name that can't be found.
原因:
- Terraform apply 時に App Service の設定更新が Key Vault シークレット作成より先に実行された
- App Service が Key Vault 参照を解決しようとした時点でシークレットがまだ存在しなかった
- Key Vault 参照のキャッシュが古い状態のまま残っている
確認方法:
# Key Vault シークレットの存在確認
az keyvault secret list --vault-name ecauth-staging-kv -o table
# Key Vault 参照のステータス確認
az rest --method get --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Web/sites/<app-name>/config/configreferences/connectionstrings?api-version=2022-03-01"解決方法:
App Service の接続文字列を再設定して Key Vault 参照を強制的に再解決させます:
# 接続文字列を再設定(同じ値でも再解決される)
az webapp config connection-string set \
--name <app-name> \
--resource-group <resource-group> \
--connection-string-type SQLAzure \
--settings "EcAuthDbContext=@Microsoft.KeyVault(VaultName=<vault-name>;SecretName=sql-connection-string)"
# ステータス確認(Resolved になれば成功)
az rest --method get --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.Web/sites/<app-name>/config/configreferences/connectionstrings?api-version=2022-03-01"予防策:
Terraform で depends_on を設定して、Key Vault
シークレット作成後に App Service を更新するようにします:
module "web_app" {
# ...
depends_on = [module.key_vault]
}
移行手順
既存の .env から 1Password への移行
- 1Password にアイテムを作成
- 既存の .env から値をコピー
- .env.tpl テンプレートを作成
op injectで動作確認- 既存の .env を削除(または .gitignore に追加済みか確認)
# 移行確認コマンド
op inject -i .env.dev.tpl -o .env.test
diff .env .env.test
rm .env.test実装チェックリスト
Phase 1: 1Password Service Account 設定
-
- 1Password.com → Settings → Service Accounts
- New Service Account を作成
- Vault Access: 必要な保管庫に Read Items 権限
- Create Token でトークン生成
-
export OP_SERVICE_ACCOUNT_TOKEN="<トークン>" op read "op://EcAuth/ecauth-staging-sql/username" op read "op://EcAuth/ecauth-staging-sql/password" op read "op://EcAuth/ecauth-staging-sql/hostname" op read "op://EcAuth/ecauth-staging-sql/database" -
gh secret set OP_SERVICE_ACCOUNT_TOKEN \ --org EcAuth \ --visibility all \ --body "<トークン>"
Phase 2: Azure Workload Identity 設定
-
- Azure Portal → Azure Active Directory → App registrations
- New registration を作成
- Federated credentials を設定(GitHub Actions 用)
-
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_SUBSCRIPTION_IDを保存- ワークフローから
op://EcAuth/ecauth-workload-identity/*で参照
Phase 3: Terraform 構成
-
terraform.tfにonepasswordprovider 追加(version = "~> 3.0")service_account_token = var.op_service_account_tokenを設定variables.tfにop_service_account_token変数追加
-
main.tfにdata "onepassword_item"追加- vault は UUID で指定
-
terraform init -upgrade terraform plan terraform apply
Phase 4: 検証
-
curl https://ecauth-staging-xxx.azurewebsites.net/healthz -
- App Service → 構成 → 接続文字列 →
@Microsoft.KeyVault(...)と表示
- App Service → 構成 → 接続文字列 →
補足: EcAuth.MockIdP の 1Password 設定
MockIdP (EcAuth.MockIdP/migrate.yml)
は環境別に異なるトークンを使用します:
# staging 環境: 1Password からシークレットを取得
- name: Load secrets from 1Password (staging)
if: github.event.inputs.environment == 'staging'
uses: 1password/load-secrets-action@v3
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_NONPROD }}
SQL_HOST: op://EcAuth/ecauth-dev-sql/hostname
SQL_DATABASE: op://EcAuth/ecauth-dev-sql/database
SQL_USERNAME: op://EcAuth/ecauth-dev-sql/username
SQL_PASSWORD: op://EcAuth/ecauth-dev-sql/password
# production 環境: Prod トークンを使用
- name: Load secrets from 1Password (production)
if: github.event.inputs.environment == 'production'
uses: 1password/load-secrets-action@v3
with:
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PROD }}
SQL_HOST: op://EcAuth/ecauth-prod-sql/hostname
SQL_DATABASE: op://EcAuth/ecauth-prod-sql/database
SQL_USERNAME: op://EcAuth/ecauth-prod-sql/username
SQL_PASSWORD: op://EcAuth/ecauth-prod-sql/password