<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Organizations on Tarragon</title><link>https://tarrragon.github.io/blog/tags/organizations/</link><description>Recent content in Organizations on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 26 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/organizations/index.xml" rel="self" type="application/rss+xml"/><item><title>跨帳號策略 — Organizations、SCP 與帳號工廠</title><link>https://tarrragon.github.io/blog/infra/02-identity-credentials/multi-account-strategy/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/infra/02-identity-credentials/multi-account-strategy/</guid><description>&lt;p>單一帳號走到某個規模後，帳號本身會變成隔離的瓶頸。IAM policy 能控制「誰能做什麼」，但同一個帳號裡的所有資源共用同一組 service quota、同一份 CloudTrail、同一張帳單，一個團隊的操作失誤或資源耗盡會波及整個帳號。把環境拆成獨立帳號，讓每個帳號只承載一個職責，是 IAM 之上的第二層隔離 — &lt;a href="https://tarrragon.github.io/blog/infra/02-identity-credentials/iam-oidc-privilege-boundary/" data-link-title="身分與憑證地基 — IAM 模型、OIDC 短期憑證與權限邊界設計" data-link-desc="IAM 的 identity / policy / role 三元件、最小權限的持續收斂、用 OIDC 取代長期 access key，以及 SCP 與 Permissions Boundary 的環境隔離">模組二的身分與憑證地基&lt;/a>控制的是「誰能做什麼」，帳號邊界控制的是「做錯了波及多遠」。&lt;/p>
&lt;h2 id="單帳號-vs-多帳號什麼時候該切">單帳號 vs 多帳號：什麼時候該切&lt;/h2>
&lt;p>單帳號在早期是合理的起點 — 資源少、人少、管理成本低。帳號邊界帶來的隔離收益要跟它的管理成本比較：每多一個帳號就多一份 CloudTrail、多一組 IAM 基線、多一個需要管理的 state backend。&lt;/p>
&lt;p>三個訊號出現時，單帳號的邊際風險開始超過多帳號的管理成本：&lt;/p>
&lt;p>第一，production 和 dev 的資源開始互相影響。一個 dev 環境的壓力測試把帳號的 EC2 instance quota 吃滿，production 的 auto-scaling 因為拿不到新 instance 而失敗 — 這個故障跟程式碼品質無關，純粹是兩個環境共用同一組配額。帳號分開後，dev 吃滿自己的 quota 不會碰到 production。&lt;/p>
&lt;p>第二，權限邊界用 IAM 已經管不住。一個工程師的 IAM policy 限制他只能操作 &lt;code>env=dev&lt;/code> 的資源，但他手滑用了一個沒有 tag 條件的 policy、或者某個 IAM role 的 trust policy 太寬，他就能碰到 production 資源。帳號邊界是比 IAM policy 更硬的護欄 — 即使 IAM 設定出錯，帳號邊界本身就是物理隔離。&lt;/p>
&lt;p>第三，合規或稽核要求明確區分環境。SOC 2 或金融監管可能要求 production 環境有獨立的存取紀錄和變更審計，與開發環境完全分離。同帳號裡做這件事要靠大量的 IAM 條件和 CloudTrail filter，跨帳號則天然滿足。&lt;/p>
&lt;h2 id="ou-結構帳號怎麼分群">OU 結構：帳號怎麼分群&lt;/h2>
&lt;p>AWS Organizations 用 Organizational Unit（OU）把帳號分群，OU 是 SCP 的掛載點 — 一條 SCP 掛在 OU 上，底下所有帳號都受約束。OU 的設計決定了護欄的作用範圍。&lt;/p>
&lt;p>常見的 OU 拓撲有四層：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>OU&lt;/th>
 &lt;th>底下的帳號&lt;/th>
 &lt;th>職責&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Security&lt;/td>
 &lt;td>Log Archive、Security Tooling&lt;/td>
 &lt;td>集中存放 CloudTrail / Config 日誌、安全工具帳號&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Workload / Prod&lt;/td>
 &lt;td>每個產品線或服務的 production 帳號&lt;/td>
 &lt;td>承載正式流量，SCP 最嚴格&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Workload / NonProd&lt;/td>
 &lt;td>dev、staging 帳號&lt;/td>
 &lt;td>承載開發與驗證，SCP 較寬鬆但仍有底線&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sandbox&lt;/td>
 &lt;td>個人實驗帳號&lt;/td>
 &lt;td>可隨時重建，SCP 限制預算上限和禁止的服務&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>環境怎麼對應到帳號，跟&lt;a href="https://tarrragon.github.io/blog/infra/04-environment-separation/directory-module-parameterization/" data-link-title="環境分離與模組化 — 目錄結構、module 參數化與 retrofit 路徑" data-link-desc="用目錄結構在第一天就隔開 dev 與 prod 的 state，用 module 讓環境共用同一套邏輯只差參數，以及已經單環境跑起來後怎麼安全拆分">模組四的環境分離&lt;/a>是同一個問題的不同層次 — 模組四用目錄和 state 分離環境的 IaC，這裡用帳號分離環境的雲端資源。兩者可以疊加：每個帳號裡的 IaC 仍然用獨立目錄和 state 管理。&lt;/p>
&lt;p>OU 結構的設計原則是「按信任等級分群、按職責隔離」。Prod 跟 NonProd 分開是因為信任等級不同（prod 的 SCP 更嚴格）。Security 獨立是因為它的職責是「監控其他所有帳號」— 如果 security 帳號被攻破，攻擊者能修改稽核日誌來掩蓋行蹤，所以它的存取權限要收到最小。&lt;/p>
&lt;p>一個常見的錯誤是把 OU 當成組織架構的映射（按部門分 OU）。OU 的分群依據是安全邊界和 SCP 策略，不是彙報線。兩個部門如果需要相同的 SCP，它們的帳號應該在同一個 OU 底下；一個部門如果有 prod 和 dev 環境，它們應該在不同 OU 底下。&lt;/p></description><content:encoded><![CDATA[<p>單一帳號走到某個規模後，帳號本身會變成隔離的瓶頸。IAM policy 能控制「誰能做什麼」，但同一個帳號裡的所有資源共用同一組 service quota、同一份 CloudTrail、同一張帳單，一個團隊的操作失誤或資源耗盡會波及整個帳號。把環境拆成獨立帳號，讓每個帳號只承載一個職責，是 IAM 之上的第二層隔離 — <a href="/blog/infra/02-identity-credentials/iam-oidc-privilege-boundary/" data-link-title="身分與憑證地基 — IAM 模型、OIDC 短期憑證與權限邊界設計" data-link-desc="IAM 的 identity / policy / role 三元件、最小權限的持續收斂、用 OIDC 取代長期 access key，以及 SCP 與 Permissions Boundary 的環境隔離">模組二的身分與憑證地基</a>控制的是「誰能做什麼」，帳號邊界控制的是「做錯了波及多遠」。</p>
<h2 id="單帳號-vs-多帳號什麼時候該切">單帳號 vs 多帳號：什麼時候該切</h2>
<p>單帳號在早期是合理的起點 — 資源少、人少、管理成本低。帳號邊界帶來的隔離收益要跟它的管理成本比較：每多一個帳號就多一份 CloudTrail、多一組 IAM 基線、多一個需要管理的 state backend。</p>
<p>三個訊號出現時，單帳號的邊際風險開始超過多帳號的管理成本：</p>
<p>第一，production 和 dev 的資源開始互相影響。一個 dev 環境的壓力測試把帳號的 EC2 instance quota 吃滿，production 的 auto-scaling 因為拿不到新 instance 而失敗 — 這個故障跟程式碼品質無關，純粹是兩個環境共用同一組配額。帳號分開後，dev 吃滿自己的 quota 不會碰到 production。</p>
<p>第二，權限邊界用 IAM 已經管不住。一個工程師的 IAM policy 限制他只能操作 <code>env=dev</code> 的資源，但他手滑用了一個沒有 tag 條件的 policy、或者某個 IAM role 的 trust policy 太寬，他就能碰到 production 資源。帳號邊界是比 IAM policy 更硬的護欄 — 即使 IAM 設定出錯，帳號邊界本身就是物理隔離。</p>
<p>第三，合規或稽核要求明確區分環境。SOC 2 或金融監管可能要求 production 環境有獨立的存取紀錄和變更審計，與開發環境完全分離。同帳號裡做這件事要靠大量的 IAM 條件和 CloudTrail filter，跨帳號則天然滿足。</p>
<h2 id="ou-結構帳號怎麼分群">OU 結構：帳號怎麼分群</h2>
<p>AWS Organizations 用 Organizational Unit（OU）把帳號分群，OU 是 SCP 的掛載點 — 一條 SCP 掛在 OU 上，底下所有帳號都受約束。OU 的設計決定了護欄的作用範圍。</p>
<p>常見的 OU 拓撲有四層：</p>
<table>
  <thead>
      <tr>
          <th>OU</th>
          <th>底下的帳號</th>
          <th>職責</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Security</td>
          <td>Log Archive、Security Tooling</td>
          <td>集中存放 CloudTrail / Config 日誌、安全工具帳號</td>
      </tr>
      <tr>
          <td>Workload / Prod</td>
          <td>每個產品線或服務的 production 帳號</td>
          <td>承載正式流量，SCP 最嚴格</td>
      </tr>
      <tr>
          <td>Workload / NonProd</td>
          <td>dev、staging 帳號</td>
          <td>承載開發與驗證，SCP 較寬鬆但仍有底線</td>
      </tr>
      <tr>
          <td>Sandbox</td>
          <td>個人實驗帳號</td>
          <td>可隨時重建，SCP 限制預算上限和禁止的服務</td>
      </tr>
  </tbody>
</table>
<p>環境怎麼對應到帳號，跟<a href="/blog/infra/04-environment-separation/directory-module-parameterization/" data-link-title="環境分離與模組化 — 目錄結構、module 參數化與 retrofit 路徑" data-link-desc="用目錄結構在第一天就隔開 dev 與 prod 的 state，用 module 讓環境共用同一套邏輯只差參數，以及已經單環境跑起來後怎麼安全拆分">模組四的環境分離</a>是同一個問題的不同層次 — 模組四用目錄和 state 分離環境的 IaC，這裡用帳號分離環境的雲端資源。兩者可以疊加：每個帳號裡的 IaC 仍然用獨立目錄和 state 管理。</p>
<p>OU 結構的設計原則是「按信任等級分群、按職責隔離」。Prod 跟 NonProd 分開是因為信任等級不同（prod 的 SCP 更嚴格）。Security 獨立是因為它的職責是「監控其他所有帳號」— 如果 security 帳號被攻破，攻擊者能修改稽核日誌來掩蓋行蹤，所以它的存取權限要收到最小。</p>
<p>一個常見的錯誤是把 OU 當成組織架構的映射（按部門分 OU）。OU 的分群依據是安全邊界和 SCP 策略，不是彙報線。兩個部門如果需要相同的 SCP，它們的帳號應該在同一個 OU 底下；一個部門如果有 prod 和 dev 環境，它們應該在不同 OU 底下。</p>
<h2 id="scp連管理員都越不過的護欄">SCP：連管理員都越不過的護欄</h2>
<p>Service Control Policy（SCP）是掛在 OU 或帳號上的權限天花板。它跟 IAM policy 的差別是層級：IAM policy 控制「這個身分能做什麼」，SCP 控制「這個帳號裡的任何身分最多能做什麼」。即使帳號內的 root user 或 AdministratorAccess role，也受 SCP 約束。</p>
<p>SCP 的設計策略以 deny-list 為主 — 預設允許所有動作，用 SCP 明確禁止少數高風險操作。相比 allow-list（預設禁止、逐一開放），deny-list 的管理成本低得多，因為 AWS 的 service 和 action 數量龐大，逐一列舉允許清單容易漏、也容易在新服務上線時擋住正常使用。</p>
<p>三條適合從第一天就掛上去的 SCP：</p>
<h3 id="禁止關閉-cloudtrail">禁止關閉 CloudTrail</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;Version&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-10-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nt">&#34;Statement&#34;</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;Sid&#34;</span><span class="p">:</span> <span class="s2">&#34;DenyCloudTrailDisable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;Effect&#34;</span><span class="p">:</span> <span class="s2">&#34;Deny&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;Action&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="s2">&#34;cloudtrail:StopLogging&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="s2">&#34;cloudtrail:DeleteTrail&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="s2">&#34;cloudtrail:UpdateTrail&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nt">&#34;Resource&#34;</span><span class="p">:</span> <span class="s2">&#34;*&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">}]</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>CloudTrail 是事後追溯「誰做了什麼」的唯一來源。攻擊者入侵帳號後的第一步往往是關掉稽核日誌來掩蓋行蹤，用 SCP 禁止這個動作，讓日誌在帳號層級不可關閉。</p>
<h3 id="禁止離開指定-region">禁止離開指定 region</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;Version&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-10-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nt">&#34;Statement&#34;</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nt">&#34;Sid&#34;</span><span class="p">:</span> <span class="s2">&#34;DenyOutsideRegion&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nt">&#34;Effect&#34;</span><span class="p">:</span> <span class="s2">&#34;Deny&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;NotAction&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="s2">&#34;iam:*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="s2">&#34;sts:*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="s2">&#34;organizations:*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="s2">&#34;support:*&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nt">&#34;Resource&#34;</span><span class="p">:</span> <span class="s2">&#34;*&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nt">&#34;Condition&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">      <span class="nt">&#34;StringNotEquals&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nt">&#34;aws:RequestedRegion&#34;</span><span class="p">:</span> <span class="p">[</span><span class="s2">&#34;ap-northeast-1&#34;</span><span class="p">,</span> <span class="s2">&#34;us-east-1&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="p">}]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>限制資源只能建在指定 region，避免有人在沒人注意的 region（如 <code>af-south-1</code>）開資源 — 不管是誤操作還是攻擊者利用。<code>NotAction</code> 裡排除 IAM 和 STS 等全域服務，因為它們不分 region。<code>us-east-1</code> 通常要保留，因為 CloudFront、ACM（global cert）等服務的 API 端點在 us-east-1。</p>
<h3 id="禁止刪除-vpc-flow-logs">禁止刪除 VPC Flow Logs</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nt">&#34;Version&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-10-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nt">&#34;Statement&#34;</span><span class="p">:</span> <span class="p">[{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nt">&#34;Sid&#34;</span><span class="p">:</span> <span class="s2">&#34;DenyDeleteFlowLogs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nt">&#34;Effect&#34;</span><span class="p">:</span> <span class="s2">&#34;Deny&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="nt">&#34;Action&#34;</span><span class="p">:</span> <span class="s2">&#34;ec2:DeleteFlowLogs&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="nt">&#34;Resource&#34;</span><span class="p">:</span> <span class="s2">&#34;*&#34;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="p">}]</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>VPC Flow Logs 記錄網路層的流量軌跡，是安全事件排查的關鍵資料。跟 CloudTrail 的邏輯一樣 — 稽核資料不允許被帳號內的操作者刪除。</p>
<h3 id="scp-的繼承模型">SCP 的繼承模型</h3>
<p>SCP 沿著 OU 樹向下繼承：掛在 Root OU 的 SCP 對所有帳號生效，掛在子 OU 的 SCP 只對該 OU 底下的帳號生效。多層 SCP 的效果是交集 — 父 OU 禁止的動作，子 OU 無法用 SCP 重新允許。這個交集模型讓安全團隊能在頂層設「絕對底線」，各子 OU 只能在底線之內進一步收斂、不能放寬。</p>
<p>把 SCP 用 Terraform 管理：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_organizations_policy&#34; &#34;deny_cloudtrail_disable&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  name</span>        <span class="o">=</span> <span class="s2">&#34;deny-cloudtrail-disable&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">  description</span> <span class="o">=</span> <span class="s2">&#34;Prevent anyone from stopping or deleting CloudTrail&#34;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">  type</span>        <span class="o">=</span> <span class="s2">&#34;SERVICE_CONTROL_POLICY&#34;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">  content</span>     <span class="o">=</span> <span class="k">file</span><span class="p">(</span><span class="s2">&#34;policies/deny-cloudtrail-disable.json&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">}
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_organizations_policy_attachment&#34; &#34;root_deny_cloudtrail&#34;</span> {
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">  policy_id</span> <span class="o">=</span> <span class="k">aws_organizations_policy</span><span class="p">.</span><span class="k">deny_cloudtrail_disable</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">  target_id</span> <span class="o">=</span> <span class="k">aws_organizations_organization</span><span class="p">.</span><span class="k">main</span><span class="p">.</span><span class="k">roots</span><span class="p">[</span><span class="m">0</span><span class="p">].</span><span class="k">id</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">}</span></span></code></pre></div><p>SCP 的 JSON 存在 repo 的 <code>policies/</code> 目錄，變更走 PR review，讓護欄本身也在版本控制與審查流程裡。</p>
<p>控制面 token 的治理是 SCP 護欄之外需要同步處理的議題。Cloudflare 2023 事件中，控制面 token 的生命週期與最小權限沒有對齊，機器憑證形成跨服務的高權限風險（見 <a href="/blog/backend/07-security-data-protection/cases/cloudflare-control-plane-token-2023/" data-link-title="7.C2 Cloudflare：2023 Control-plane Token 事件" data-link-desc="控制面 token 事件如何回寫 secrets 與機器憑證治理。">Cloudflare：Control-plane Token 事件</a>）。Okta 2023 事件則顯示身份治理若只覆蓋生產系統而忽略支援工具鏈，支援系統的 session 和 token 會成為跨租戶的風險放大點（見 <a href="/blog/backend/07-security-data-protection/cases/okta-support-system-incident-2023/" data-link-title="7.C5 Okta：2023 Support System 事件" data-link-desc="支援系統憑證風險如何擴散到客戶租戶的案例。">Okta：Support System 事件</a>）。兩個案例的共同教訓是：SCP 管的是 AWS API 層的動作上限，但 token / session 這類應用層的機器憑證需要獨立的 lifecycle 治理。</p>
<h2 id="帳號工廠每個新帳號自帶安全基線">帳號工廠：每個新帳號自帶安全基線</h2>
<p>跨帳號策略（帳號數量、OU 結構、SCP 規則）屬於影響全組織的架構決策，建議在實施前取得技術主管或 CTO 的對齊。SCP 一旦套用到 OU，該 OU 下所有帳號立即受影響，回退需要修改 SCP 或移動帳號到不同 OU。</p>
<p>手動建帳號的問題跟手動建資源一樣 — 每次都靠人記得「開完帳號後要開 CloudTrail、要刪預設 VPC、要設基線 IAM role」。帳號工廠（Account Factory）把這些步驟自動化成一個可重複的流程：建一個帳號、自動套用安全基線、自動加進正確的 OU。</p>
<p>AWS Control Tower 是 AWS 提供的帳號工廠實作，它包裝了 Organizations、SCP、Config Rules 和 CloudFormation StackSet，提供一個「建帳號 → 自動配置」的流水線。它的好處是一鍵啟用、內建一組 AWS 建議的護欄；代價是它對 OU 結構和 SCP 有自己的意見，跟團隊已有的設計可能衝突，而且它用 CloudFormation StackSet 做基線配置，跟 Terraform 管理的資源需要劃清邊界。</p>
<p>不用 Control Tower 時，帳號工廠可以用 Terraform + 腳本自建。核心是一個 module 接受帳號名稱和 OU 作為參數，產出：帳號建立、CloudTrail trail、預設 VPC 刪除、基線 IAM role（讓管理帳號能 assume 進來做維護）、Config recorder 啟用。</p>
<p>每個新帳號該自帶的安全基線至少包含：</p>
<ul>
<li>CloudTrail 開啟並寫到集中的 Log Archive 帳號</li>
<li>預設 VPC 刪除（預設 VPC 的 security group 全通、CIDR 固定且跨帳號重複，留著是隱患）</li>
<li>基線 IAM role 讓管理帳號能 assume 進來</li>
<li>Config recorder 啟用（記錄資源設定變更歷史）</li>
<li>掛上所屬 OU 的 SCP</li>
</ul>
<p>導入時程參考：初次設定 Organizations + OU 結構 + day-1 SCP 約需 2-3 天；之後每開一個新帳號（含基線配置）約需 2-4 小時。</p>
<h2 id="跨帳號存取role-assumption">跨帳號存取：role assumption</h2>
<p>多帳號架構裡，人或自動化需要在不同帳號之間切換操作。跨帳號存取用 IAM role 的 trust policy 實現 — 目標帳號建一個 role，trust policy 允許來源帳號的特定身分 assume 這個 role。</p>
<p>AWS Organizations 在建子帳號時會自動建一個 <code>OrganizationAccountAccessRole</code>，讓管理帳號的 admin 能 assume 進去。這個 role 的權限是 AdministratorAccess — 它的用途是初始設定和緊急存取，日常操作不該用它。日常的跨帳號存取應該建立職責專用的 role：部署用的 role 只有部署相關權限、唯讀稽核用的 role 只有 read 權限。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-hcl" data-lang="hcl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_iam_role&#34; &#34;deploy_from_cicd&#34;</span> {
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">  name</span> <span class="o">=</span> <span class="s2">&#34;deploy-from-cicd-account&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">  assume_role_policy</span> <span class="o">=</span> <span class="k">jsonencode</span><span class="p">(</span>{
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">    Version</span> <span class="o">=</span> <span class="s2">&#34;2012-10-17&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">    Statement</span> <span class="o">=</span> <span class="p">[</span>{
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">      Effect</span>    <span class="o">=</span> <span class="s2">&#34;Allow&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="n">      Principal</span> <span class="o">=</span><span class="n"> { AWS</span> <span class="o">=</span> <span class="s2">&#34;arn:aws:iam::111111111111:role/cicd-runner&#34;</span> }
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">      Action</span>    <span class="o">=</span> <span class="s2">&#34;sts:AssumeRole&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">      Condition</span> <span class="o">=</span> {
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">        StringEquals</span> <span class="o">=</span><span class="n"> { &#34;sts:ExternalId&#34;</span> <span class="o">=</span> <span class="s2">&#34;deploy-prod-2026&#34;</span> }
</span></span><span class="line"><span class="ln">12</span><span class="cl">      }
</span></span><span class="line"><span class="ln">13</span><span class="cl">    }<span class="p">]</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  }<span class="p">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">}</span></span></code></pre></div><p><code>ExternalId</code> 是防止 confused deputy 攻擊的機制 — 如果 trust policy 只用帳號 ID 驗證，任何能在來源帳號建 role 的人都能 assume 目標 role。加上 ExternalId 讓 assumption 多一個只有雙方知道的驗證值。</p>
<p>跨帳號存取的設計與<a href="/blog/infra/02-identity-credentials/iam-oidc-privilege-boundary/" data-link-title="身分與憑證地基 — IAM 模型、OIDC 短期憑證與權限邊界設計" data-link-desc="IAM 的 identity / policy / role 三元件、最小權限的持續收斂、用 OIDC 取代長期 access key，以及 SCP 與 Permissions Boundary 的環境隔離">模組二的 OIDC 短期憑證</a>互補 — OIDC 解決「雲外到雲內」的身分聯合（CI/CD → AWS），role assumption 解決「雲內帳號之間」的身分切換。</p>
<h2 id="帳單整合">帳單整合</h2>
<p>Organizations 的附帶收益是合併帳單（Consolidated Billing）。所有子帳號的用量合併到管理帳號的帳單裡，一方面簡化付款流程（一張帳單而非多張），另一方面可以享受跨帳號的用量折扣 — 例如 S3 的定價階梯是看總用量，三個帳號各用 1TB 分開計費跟合併成 3TB 計費，後者的單位價格更低。</p>
<p>合併帳單跟成本歸屬的 tagging 互補。合併帳單讓所有費用匯到一張帳單，tagging 讓這張帳單能拆到各團隊和用途 — 這兩件事在<a href="/blog/infra/08-governance-habits/cost-visibility-rhythm/" data-link-title="成本可見性與最小可行治理節奏" data-link-desc="用 tag 驅動的成本分攤讓帳單有人負責，以及判斷什麼治理該 day-1 就立、什麼等規模逼出來再加">模組八的成本可見性</a>展開。帳號邊界本身也是一層成本隔離：每個帳號的用量可以獨立查看，讓「這個帳號這個月花了多少」變成自動可查、不需要依賴 tag。</p>
<h2 id="跨分類引用">跨分類引用</h2>
<ul>
<li>→ <a href="/blog/infra/02-identity-credentials/iam-oidc-privilege-boundary/" data-link-title="身分與憑證地基 — IAM 模型、OIDC 短期憑證與權限邊界設計" data-link-desc="IAM 的 identity / policy / role 三元件、最小權限的持續收斂、用 OIDC 取代長期 access key，以及 SCP 與 Permissions Boundary 的環境隔離">身分與憑證地基</a>：IAM role / policy / OIDC 是帳號內的身分控制，本篇是帳號間的隔離</li>
<li>→ <a href="/blog/infra/04-environment-separation/directory-module-parameterization/" data-link-title="環境分離與模組化 — 目錄結構、module 參數化與 retrofit 路徑" data-link-desc="用目錄結構在第一天就隔開 dev 與 prod 的 state，用 module 讓環境共用同一套邏輯只差參數，以及已經單環境跑起來後怎麼安全拆分">環境分離與模組化</a>：目錄與 state 分離環境的 IaC，帳號分離是雲端資源層的對應</li>
<li>→ <a href="/blog/infra/08-governance-habits/cost-visibility-rhythm/" data-link-title="成本可見性與最小可行治理節奏" data-link-desc="用 tag 驅動的成本分攤讓帳單有人負責，以及判斷什麼治理該 day-1 就立、什麼等規模逼出來再加">成本可見性</a>：合併帳單 + tagging 的成本歸屬</li>
<li>→ <a href="/blog/infra/07-infra-as-pr/plan-review-apply-guardrails/" data-link-title="infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">infra 走 PR 流程</a>：SCP 的 JSON 存 repo、變更走 PR review</li>
</ul>
]]></content:encoded></item></channel></rss>