top of page

構成の境界線 — 山崎行政書士事務所 パラメータ夜話

00:43アラートは、寝静まった事務所の天井を針で突くみたいに鳴った。[High sev] Key Vault: SecretGet from GitHub-hosted runner IP奏汰(そうた)がモニタに顔を寄せる。「prd-kv-01 の smtp-connector-password、公開ランナーのIPから読まれてる。ジョブのメタは repo:port-east/payment:ref:refs/heads/staging。」

律斗(りつと)が短く頷く。「ウォールーム、起こす。**“パラメータ会議”**だ。」

1|一次遮断は“値”から

00:51会議室A、ホワイトボードの左端にりなが書く。《遮断→証跡→運用差し替え》。悠真(ゆうま)がKQLを叩く。

ServicePrincipalSignInLogs
| where AppDisplayName == "github-oidc-sp"
| where TimeGenerated > ago(12h)
| summarize min(TimeGenerated), max(TimeGenerated), dcount(IPAddress), makeset(IPAddress)
          by ServicePrincipalName, AppId

github-oidc-sp が staging からも production からも来てる。」悠真。奏汰が苦い顔。「サービスプリンシパルのシークレットでログインしてた頃の名残、まだ生きてる。AZURE_CREDENTIALS のローテ忘れ…。」

りな:「まず“読み取りそのもの”を止める。Key Vaultの公開ネットワークを切る。つぎに“誰の権限”で読めたのかを詰める。最後に“どう運用を差し替えるか”。」

叶多(かなた)がTerraformのモジュールを開く。「差し替えパラメータ、貼るよ。」

# prd-kv-01 (Key Vault)
resource "azurerm_key_vault" "prd" {
  name                       = "prd-kv-01"
  resource_group_name        = var.rg_name
  location                   = var.location
  sku_name                   = "premium"
  soft_delete_retention_days = 90
  purge_protection_enabled   = true
  public_network_access_enabled = false
  minimum_tls_version        = "TLS1_2"

  network_acls {
    bypass         = "None"
    default_action = "Deny"
    ip_rules       = []  # 全閉
    virtual_network_subnet_ids = [azurerm_subnet.pe_subnet.id]
  }
}

「public_network_access_enabled = false で即座に外からのGetは沈む。」蓮斗(れんと)が確認。「ただ、パイプラインが死ぬ。先にOIDCに乗り換えよう。」

2|シークレットからOIDCへ:主語はsubject

01:07奏汰がGitHub ActionsのYAMLを指で叩く。「id-token: write を付けて、SPのシークレットは捨てる。」

# .github/workflows/deploy.yml(抜粋)
permissions:
  id-token: write
  contents: read
env:
  AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }}
  AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
  AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/staging'
    steps:
      - uses: actions/checkout@v4
      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ env.AZURE_CLIENT_ID }}
          tenant-id: ${{ env.AZURE_TENANT_ID }}
          subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}

悠真:「でも誰のトークンでも入れるのは怖い。subject を固定する。」蓮斗:「repo:{owner}/{repo}:ref:refs/heads/staging しか受けない。」

りな:「NIS2の‘変更の管理’にも噛むわね。“どのブランチからの変更だけが入れるか”を規範化する。」

奏汰がTerraformの差分を流す。

# OIDCフェデレーション設定(AzureAD)
resource "azuread_application" "github_oidc_app" {
  display_name = "github-oidc-sp"
}

resource "azuread_service_principal" "github_oidc_sp" {
  application_id = azuread_application.github_oidc_app.application_id
}

resource "azuread_application_federated_identity_credential" "staging_cred" {
  application_object_id = azuread_application.github_oidc_app.object_id
  display_name          = "repo-port-east-payment-staging"
  issuer                = "https://token.actions.githubusercontent.com"
  subject               = "repo:port-east/payment:ref:refs/heads/staging"
  audiences             = ["api://AzureADTokenExchange"]
}

律斗:「ロールは最小。Contributor は無し、Key Vault Secrets User と対象スコープを限定。」

resource "azurerm_role_assignment" "kv_secret_user_stg" {
  scope                = azurerm_key_vault.prd.id
  role_definition_name = "Key Vault Secrets User"
  principal_id         = azuread_service_principal.github_oidc_sp.object_id
  condition_version    = "2.0"
  # “このタグが 'staging' のときのみ許可”みたいな条件に拡張可
}

りな:「“本番Key Vaultに対してstagingブランチからのGetを禁止”言い切れる文で運用通知を書く。」

3|ネットワークを絞る:Private Endpoint と DNS

01:28叶多:「Key VaultのPrivate Endpointを張る。Zoneprivatelink.vaultcore.azure.net。」

resource "azurerm_private_dns_zone" "kv" {
  name                = "privatelink.vaultcore.azure.net"
  resource_group_name = var.rg_name
}

resource "azurerm_private_endpoint" "kv" {
  name                = "pe-prd-kv-01"
  location            = var.location
  resource_group_name = var.rg_name
  subnet_id           = azurerm_subnet.pe_subnet.id

  private_service_connection {
    name                           = "kv-psc"
    is_manual_connection           = false
    private_connection_resource_id = azurerm_key_vault.prd.id
    subresource_names              = ["vault"]
  }

  private_dns_zone_group {
    name                 = "kv-dns"
    private_dns_zone_ids = [azurerm_private_dns_zone.kv.id]
  }
}

悠真:「サブネット側のPEポリシーは無効化済み?」叶多:「済み。ルートはFW経由。0.0.0.0/0 -> azfw。」りな:「“Key Vaultへは社内からだけ”、通知文に**“どこからでも”を消す**。」

4|“環境名”は呪文じゃない:TF_ENV を“実体化”する

01:46奏汰:「staging のつもりが prod に差す事故、変数の表記揺れが原因。TF_ENV をタグと名前に必ず混ぜる。」

variable "env" {
  type    = string
  validation {
    condition     = contains(["dev", "stg", "prd"], var.env)
    error_message = "env must be one of dev|stg|prd"
  }
}

locals {
  name = "port-${var.env}-kv-01"
  tags = {
    "Environment" = var.env
    "Owner"       = "LegalCloudOps"
    "DataClass"   = "C2"
  }
}

蓮斗:「タグ Environment=stg 以外は受けないKey Vaultアクセス条件、次で切る。」りな:「例外には“期限”stg が prd に触れるのは今夜の復旧ウィンドウだけ。」

5|可視化は“窓のサイズ”で決まる

02:05悠真がログの“窓”を調整する。「window_duration = PT5Mevaluation_frequency = PT1M1分遅延で動く。」

# Azure Monitor Scheduled Query Alert (v2)
resource "azurerm_monitor_scheduled_query_rules_alert_v2" "sp_ip_spike" {
  name                = "sp-signin-unknown-ip"
  resource_group_name = var.rg_name
  location            = var.location
  evaluation_frequency = "PT1M"
  window_duration      = "PT5M"
  severity             = 2
  scopes               = [azurerm_log_analytics_workspace.law.id]
  query               = <<-KQL
    ServicePrincipalSignInLogs
    | where AppDisplayName == "github-oidc-sp"
    | summarize cnt=dcount(IPAddress) by bin(TimeGenerated, 5m)
    | where cnt > 1
  KQL
  action {
    action_groups = [azurerm_monitor_action_group.ops.id]
  }
}

“5分で2IP以上”を“異常”にする。」悠真。りな:「誤検知の議論は明日。今夜は絞るのが先。」

6|読み取りの“証跡”は、テーブルに残す

02:24蓮斗がテーブルを切り替える。「KeyVaultDataPlane、SecretGet の列。」

KeyVaultDataPlane
| where ResourceId has "prd-kv-01"
| where OperationName == "SecretGet"
| project TimeGenerated, CallerIpAddress, Identity, ResultSignature, SecretName
| sort by TimeGenerated desc

staging のジョブIDが紐づいてる。」蓮斗。りな:「“誰が・いつ・何を見たか”は事実“漏えいしたか”は確認中文の段落分ける。」

7|CAポリシーは“誰が・どこから・何を”の三点

02:41叶多:「Azure管理面準拠デバイスのみFIDO2Cloud apps: Azure Management に絞る。」

{
  "displayName": "CA-RequireCompliant-FIDO2-For-AzureMgmt",
  "state": "enabled",
  "conditions": {
    "users": { "includeRoles": ["62e90394-69f5-4237-9190-012177145e10"] }, 
    "applications": { "includeApplications": ["797f4846-ba00-4fd7-ba43-dac1f8f63013"] },
    "clientAppTypes": ["browser", "mobileAppsAndDesktopClients"]
  },
  "grantControls": {
    "operator": "AND",
    "builtInControls": ["mfa", "compliantDevice"]
  },
  "authenticationStrength": { "displayName": "Phishing-resistant MFA" }
}

りな:「“人の手の運用”はここまで。“機械の運用”はOIDCのsubjectで切る。二層で線を引く。」

8|“消す”勇気と“残す”設計

03:05奏汰:「GitHubのリポジトリシークレット、AZURE_CREDENTIALSを削除ブランチ保護厳格に。」

  • Required reviewers: 2

  • Dismiss stale approvals: On

  • Require status checks: tf-fmt, tf-plan, sec-scan

  • Require linear history: On

  • Force pushes: Block

  • Delete branches on merge: On

りな:「“削除”は“責任”“戻せる証跡”も残す。tf-plan のアーティファクト保存**、90日に延長。」

9|“例外”の設計:期限はUTCで

03:22蓮斗:「例外許可:今から02:30–04:30 UTC。staging から prd Key Vaultの**SecretGetのみ**通す。」

# 期限付きロール(PIM化の前段)
resource "azurerm_role_assignment_schedule" "tmp_kv_reader" {
  name                 = "tmp-stg-to-prd-kv-read"
  scope                = azurerm_key_vault.prd.id
  principal_id         = azuread_service_principal.github_oidc_sp.object_id
  role_definition_name = "Key Vault Secrets User"
  start_date_time      = "2025-09-13T02:30:00Z"
  expiration           = "AfterDuration"
  duration             = "PT2H"
}

りな:「UTCで統一“日本時間で…”は誤解を招く。」

10|夜明け前、線は閉じられる

04:06ServicePrincipalのサインインは収束、Key VaultのSecretGetは社内IPだけになった。奏汰が最後のコミットメッセージを書く。

chore: switch to OIDC; disable kv public; add PE & dns; tag env; alert v2

やまにゃんが会議室の隅でUSBのしっぽを揺らす札を置く。「みゃ〜てら!(Terraform)」

11|朝会:“言い切り”と“数字”

09:10ふみか(広報)が一次報文を読み上げる。

  • 事象:本番Key Vaultのシークレット読み取りに外部IPからのアクセスを検知

  • 対応:公開ネットワーク遮断(public_network_access_enabled=false)Private Endpoint導入GitHub OIDC移行(subject固定)期限付き例外で復旧を完了。

  • 影響:読み取りは確認第三者提供の確証は無し(継続調査)。

  • 再発防止:タグ基準のアクセス制御(Environment=stg 不可)Scheduled Query Alert(1分×5分窓)ブランチ保護強化

りな:「“確認”と“未確定”を同じ段で書かない数字(PT5M、PT1M、90日、UTC)は必ず添える。」

律斗:「パラメータで守った夜だ。設計物語になる。今日のKPT、Kに“値で止めた”と残す。」

12|Appendix(内部メモ/抜粋)

A. KQL(調査用)

// OIDCトークンのsubject検証
ServicePrincipalSignInLogs
| where AppDisplayName == "github-oidc-sp"
| project TimeGenerated, IPAddress, ClientAppUsed, TokenIssuerType, Claims = tostring(parse_json(AuthenticationDetails)[0].authenticationRequirement)
// Key Vaultデータプレーンの追跡
KeyVaultDataPlane
| where OperationName in ("SecretGet","SecretList")
| summarize makeset(SecretName), min(TimeGenerated), max(TimeGenerated) by Identity

B. Terraform(安全側のデフォルト)

# Storage Account(念押し)
resource "azurerm_storage_account" "prd" {
  name                      = "portprdstore01"
  resource_group_name       = var.rg_name
  location                  = var.location
  account_tier              = "Standard"
  account_replication_type  = "ZRS"
  min_tls_version           = "TLS1_2"
  public_network_access_enabled = false
  allow_blob_public_access  = false
  shared_access_key_enabled = false
  tags                      = local.tags
}

C. 監視ルールの設計指針

  • 窓(window_duration)は5〜10分頻度(evaluation_frequency)は1分

  • 信号の粒度ServicePrincipalSignInLogsKeyVaultDataPlane別アラートに。

  • しきい値は**“2種類以上のIP”“1分間に3回以上のSecretGet”行動**で定義する。

付記:この物語はフィクションですが、登場するパラメータや設定例は実務で用いられる粒度を想定しています。技術は“値”で動き、“値”で止まる。 そして文章が、その“値”に意味を与え、現場を同じ方向に向けます。今日は、その三つが噛み合った夜でした。

 
 
 

最新記事

すべて表示
“規程だけ”で終わらせないクラウド法務:Purview×ログ×権限でAIリスクを管理する方法

1. AIが進まない原因は「AI」ではなく「組織とルール」 生成AIの導入で企業がつまずく典型は、技術の難しさ以上に 「社内がたこつぼ化している」  ことです。 部門ごとに別々の生成AI・別々のルール データ共有や権限設計が追いつかず、使える人だけが使う リスク評価が属人化し、最後は「禁止事項の羅列」になって現場が萎縮 結果として「使わないのが安全」が組織の最適解になってしまう AIは“導入”では

 
 
 
【公取委の立入検査報道で再注目】クラウド移行の前に“Microsoftライセンス”と“出口条項”を棚卸しする理由

※本稿は一般情報です。個別案件の適法性判断・紛争対応は弁護士にご相談ください。 ■ 1. 何が起きているのか(ニュースの要点) 公正取引委員会が日本マイクロソフトに立入検査に入ったと報じられました。 報道では「Azureと競合する他社クラウドではMicrosoftソフトを使えない/料金を高くする等の条件を設けた疑い」が論点とされています。 結論はこれからですが、企業側として重要なのは―― “クラウ

 
 
 

コメント


Instagram​​

Microsoft、Azure、Microsoft 365、Entra は米国 Microsoft Corporation の商標または登録商標です。
本ページは一般的な情報提供を目的とし、個別案件は状況に応じて整理手順が異なります。

※本ページに登場するイラストはイメージです。
Microsoft および Azure 公式キャラクターではありません。

Microsoft, Azure, and Microsoft 365 are trademarks of Microsoft Corporation.
We are an independent service provider.

​所在地:静岡市

©2024 山崎行政書士事務所。Wix.com で作成されました。

bottom of page