<?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>Access-Key on Tarragon</title><link>https://tarrragon.github.io/blog/tags/access-key/</link><description>Recent content in Access-Key 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/access-key/index.xml" rel="self" type="application/rss+xml"/><item><title>Access Key 輪替手冊</title><link>https://tarrragon.github.io/blog/infra/02-identity-credentials/access-key-rotation-playbook/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/infra/02-identity-credentials/access-key-rotation-playbook/</guid><description>&lt;p>長期 access key 的風險隨時間單調上升——每多存在一天，被複製到新地方的機率就多一分，而輪替的難度也跟著副本數量增長。輪替不是「發現外洩才做」的緊急動作，而是定期執行的維運操作。本篇是操作手冊，從盤點開始、逐步完成輪替、最後建立自動化。&lt;/p>
&lt;h2 id="盤點帳號裡有哪些-key">盤點：帳號裡有哪些 key&lt;/h2>
&lt;p>第一步是拿到帳號內所有 IAM user 的 access key 清單。AWS 的 credential report 是這個問題的標準資料來源，它列出每個 user 的 key 狀態、建立時間與最後使用時間。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">aws iam generate-credential-report
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">aws iam get-credential-report &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --query &lt;span class="s1">&amp;#39;Content&amp;#39;&lt;/span> --output text &lt;span class="p">|&lt;/span> base64 -d &amp;gt; credential-report.csv&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>產出的 CSV 包含每個 IAM user 的兩把 key（access_key_1、access_key_2）各自的狀態。關注的欄位：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>user&lt;/code>&lt;/td>
 &lt;td>key 的擁有者&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>access_key_1_active&lt;/code>&lt;/td>
 &lt;td>key 是否啟用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>access_key_1_last_used_date&lt;/code>&lt;/td>
 &lt;td>最後使用時間——長期未使用代表可能是遺棄的 key&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>access_key_1_last_rotated&lt;/code>&lt;/td>
 &lt;td>建立或上次輪替的時間&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>用 csvkit 或試算表打開這份報告，按 &lt;code>access_key_1_last_rotated&lt;/code> 排序，最舊的 key 排最前面。超過 90 天未輪替的 key 列為第一批處理對象。&lt;/p>
&lt;p>以下腳本使用 gawk 的 &lt;code>systime()&lt;/code> 函式。如果系統的 awk 是 mawk（Ubuntu 預設），改用 &lt;code>gawk&lt;/code> 或用 &lt;code>date&lt;/code> 指令替代時間計算。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 快速列出所有啟用中、超過 90 天的 key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">aws iam list-users --query &lt;span class="s1">&amp;#39;Users[].UserName&amp;#39;&lt;/span> --output text &lt;span class="p">|&lt;/span> tr &lt;span class="s1">&amp;#39;\t&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;\n&amp;#39;&lt;/span> &lt;span class="p">|&lt;/span> &lt;span class="k">while&lt;/span> &lt;span class="nb">read&lt;/span> user&lt;span class="p">;&lt;/span> &lt;span class="k">do&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> aws iam list-access-keys --user-name &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="nv">$user&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --query &lt;span class="s2">&amp;#34;AccessKeyMetadata[?Status==&amp;#39;Active&amp;#39;].[UserName,AccessKeyId,CreateDate]&amp;#34;&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> --output text
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="k">done&lt;/span> &lt;span class="p">|&lt;/span> awk -F&lt;span class="s1">&amp;#39;\t&amp;#39;&lt;/span> &lt;span class="s1">&amp;#39;{
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="s1"> cmd = &amp;#34;date -d \&amp;#34;&amp;#34; $3 &amp;#34;\&amp;#34; +%s 2&amp;gt;/dev/null || date -jf \&amp;#34;%Y-%m-%dT%H:%M:%S+00:00\&amp;#34; \&amp;#34;&amp;#34; $3 &amp;#34;\&amp;#34; +%s&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="s1"> cmd | getline created; close(cmd)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="s1"> age = (systime() - created) / 86400
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="s1"> if (age &amp;gt; 90) printf &amp;#34;%s\t%s\t%.0f days\n&amp;#34;, $1, $2, age
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="s1">}&amp;#39;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="識別每把-key-的用途">識別每把 key 的用途&lt;/h2>
&lt;p>知道 key 存在之後，下一個問題是「這把 key 用在哪裡」。credential report 只告訴你 key 最後被用來呼叫什麼 service（&lt;code>access_key_1_last_used_service&lt;/code>），但不告訴你它被存放在哪裡。&lt;/p>
&lt;p>用途識別需要交叉比對多個來源：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>可能的存放位置&lt;/th>
 &lt;th>檢查方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>CI 環境變數（GitHub Actions）&lt;/td>
 &lt;td>repo Settings → Secrets and variables → Actions&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CI 環境變數（GitLab CI）&lt;/td>
 &lt;td>repo Settings → CI/CD → Variables&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>EC2 instance 的 user data&lt;/td>
 &lt;td>&lt;code>aws ec2 describe-instance-attribute --attribute userData&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Lambda 環境變數&lt;/td>
 &lt;td>&lt;code>aws lambda get-function-configuration --function-name NAME&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SSM Parameter Store&lt;/td>
 &lt;td>&lt;code>aws ssm get-parameters-by-path --path / --recursive&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>開發者筆電&lt;/td>
 &lt;td>&lt;code>~/.aws/credentials&lt;/code> — 需要口頭確認&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>程式碼 repo&lt;/td>
 &lt;td>&lt;code>git log --all -p | grep AKIA&lt;/code> — AKIA 是 access key 的固定前綴&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Slack / email 歷史&lt;/td>
 &lt;td>無法自動掃描，靠團隊回報&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>對每把要輪替的 key，在以上位置逐一確認。找不到用途的 key 可以先停用觀察（而非直接刪除），停用後如果有服務壞了就知道它用在哪裡。&lt;/p></description><content:encoded><![CDATA[<p>長期 access key 的風險隨時間單調上升——每多存在一天，被複製到新地方的機率就多一分，而輪替的難度也跟著副本數量增長。輪替不是「發現外洩才做」的緊急動作，而是定期執行的維運操作。本篇是操作手冊，從盤點開始、逐步完成輪替、最後建立自動化。</p>
<h2 id="盤點帳號裡有哪些-key">盤點：帳號裡有哪些 key</h2>
<p>第一步是拿到帳號內所有 IAM user 的 access key 清單。AWS 的 credential report 是這個問題的標準資料來源，它列出每個 user 的 key 狀態、建立時間與最後使用時間。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam generate-credential-report
</span></span><span class="line"><span class="ln">2</span><span class="cl">aws iam get-credential-report <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --query <span class="s1">&#39;Content&#39;</span> --output text <span class="p">|</span> base64 -d &gt; credential-report.csv</span></span></code></pre></div><p>產出的 CSV 包含每個 IAM user 的兩把 key（access_key_1、access_key_2）各自的狀態。關注的欄位：</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>user</code></td>
          <td>key 的擁有者</td>
      </tr>
      <tr>
          <td><code>access_key_1_active</code></td>
          <td>key 是否啟用</td>
      </tr>
      <tr>
          <td><code>access_key_1_last_used_date</code></td>
          <td>最後使用時間——長期未使用代表可能是遺棄的 key</td>
      </tr>
      <tr>
          <td><code>access_key_1_last_rotated</code></td>
          <td>建立或上次輪替的時間</td>
      </tr>
  </tbody>
</table>
<p>用 csvkit 或試算表打開這份報告，按 <code>access_key_1_last_rotated</code> 排序，最舊的 key 排最前面。超過 90 天未輪替的 key 列為第一批處理對象。</p>
<p>以下腳本使用 gawk 的 <code>systime()</code> 函式。如果系統的 awk 是 mawk（Ubuntu 預設），改用 <code>gawk</code> 或用 <code>date</code> 指令替代時間計算。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 快速列出所有啟用中、超過 90 天的 key</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">aws iam list-users --query <span class="s1">&#39;Users[].UserName&#39;</span> --output text <span class="p">|</span> tr <span class="s1">&#39;\t&#39;</span> <span class="s1">&#39;\n&#39;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> user<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  aws iam list-access-keys --user-name <span class="s2">&#34;</span><span class="nv">$user</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>    --query <span class="s2">&#34;AccessKeyMetadata[?Status==&#39;Active&#39;].[UserName,AccessKeyId,CreateDate]&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="se"></span>    --output text
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">done</span> <span class="p">|</span> awk -F<span class="s1">&#39;\t&#39;</span> <span class="s1">&#39;{
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s1">  cmd = &#34;date -d \&#34;&#34; $3 &#34;\&#34; +%s 2&gt;/dev/null || date -jf \&#34;%Y-%m-%dT%H:%M:%S+00:00\&#34; \&#34;&#34; $3 &#34;\&#34; +%s&#34;
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s1">  cmd | getline created; close(cmd)
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s1">  age = (systime() - created) / 86400
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s1">  if (age &gt; 90) printf &#34;%s\t%s\t%.0f days\n&#34;, $1, $2, age
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s1">}&#39;</span></span></span></code></pre></div><h2 id="識別每把-key-的用途">識別每把 key 的用途</h2>
<p>知道 key 存在之後，下一個問題是「這把 key 用在哪裡」。credential report 只告訴你 key 最後被用來呼叫什麼 service（<code>access_key_1_last_used_service</code>），但不告訴你它被存放在哪裡。</p>
<p>用途識別需要交叉比對多個來源：</p>
<table>
  <thead>
      <tr>
          <th>可能的存放位置</th>
          <th>檢查方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI 環境變數（GitHub Actions）</td>
          <td>repo Settings → Secrets and variables → Actions</td>
      </tr>
      <tr>
          <td>CI 環境變數（GitLab CI）</td>
          <td>repo Settings → CI/CD → Variables</td>
      </tr>
      <tr>
          <td>EC2 instance 的 user data</td>
          <td><code>aws ec2 describe-instance-attribute --attribute userData</code></td>
      </tr>
      <tr>
          <td>Lambda 環境變數</td>
          <td><code>aws lambda get-function-configuration --function-name NAME</code></td>
      </tr>
      <tr>
          <td>SSM Parameter Store</td>
          <td><code>aws ssm get-parameters-by-path --path / --recursive</code></td>
      </tr>
      <tr>
          <td>開發者筆電</td>
          <td><code>~/.aws/credentials</code> — 需要口頭確認</td>
      </tr>
      <tr>
          <td>程式碼 repo</td>
          <td><code>git log --all -p | grep AKIA</code> — AKIA 是 access key 的固定前綴</td>
      </tr>
      <tr>
          <td>Slack / email 歷史</td>
          <td>無法自動掃描，靠團隊回報</td>
      </tr>
  </tbody>
</table>
<p>對每把要輪替的 key，在以上位置逐一確認。找不到用途的 key 可以先停用觀察（而非直接刪除），停用後如果有服務壞了就知道它用在哪裡。</p>
<h2 id="輪替步驟五步流程">輪替步驟：五步流程</h2>
<p>輪替一把 key 的標準流程分五步，順序不能跳：</p>
<h3 id="第一步建立新-key">第一步：建立新 key</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam create-access-key --user-name deploy-bot</span></span></code></pre></div><p>輸出會包含新的 AccessKeyId 和 SecretAccessKey。SecretAccessKey 只在這一刻顯示一次，存進密碼管理器或 Secrets Manager，不要貼在 Slack 或 email 裡。</p>
<p>一個 IAM user 最多同時有兩把 key。如果已經有兩把，需要先刪除一把不用的才能建新的。</p>
<h3 id="第二步更新所有消費者">第二步：更新所有消費者</h3>
<p>把新 key 部署到上一節識別出的所有存放位置。CI 變數、Lambda 環境變數、SSM Parameter Store、開發者的 <code>~/.aws/credentials</code> 都要同步更新。</p>
<p>每更新一個消費者就做一次功能驗證——CI 跑一次 pipeline、Lambda 觸發一次、開發者跑一次 <code>aws sts get-caller-identity</code> 確認新 key 能用。</p>
<h3 id="第三步驗證新-key-生效">第三步：驗證新 key 生效</h3>
<p>所有消費者更新完後，等待一個完整的業務週期（至少 24 小時），確認沒有任何服務還在用舊 key。檢查方式是看舊 key 的 <code>LastUsedDate</code> 有沒有在更新之後還被使用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam get-access-key-last-used --access-key-id AKIAOLD12345</span></span></code></pre></div><p>如果 <code>LastUsedDate</code> 在你更新消費者之後仍有新的使用紀錄，代表有漏網的消費者還在用舊 key。</p>
<h3 id="第四步停用舊-key">第四步：停用舊 key</h3>
<p>確認無殘留使用後，停用（不是刪除）舊 key：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam update-access-key <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --user-name deploy-bot <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --access-key-id AKIAOLD12345 <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>  --status Inactive</span></span></code></pre></div><p>停用是安全的中間狀態——用到這把 key 的服務會開始報 <code>InvalidClientTokenId</code> 錯誤，但 key 還在、可以隨時重新啟用。如果停用後有意料之外的服務壞了，重新啟用就能立刻恢復。</p>
<h3 id="第五步寬限期後刪除">第五步：寬限期後刪除</h3>
<p>停用後保持 7-14 天的寬限期。這段時間是「如果有漏掉的消費者」的安全網。寬限期內無異常，刪除：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">aws iam delete-access-key <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  --user-name deploy-bot <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  --access-key-id AKIAOLD12345</span></span></code></pre></div><p>刪除後不可回復。如果有服務還在用這把 key，只能建一把新 key 然後去更新那個服務。</p>
<h2 id="自動化輪替secrets-manager">自動化輪替：Secrets Manager</h2>
<p>手動輪替的瓶頸在「找到所有消費者」這一步。如果 key 的消費者都從 Secrets Manager 讀取（而非各自存一份副本），輪替就簡化成「在 Secrets Manager 裡更新值」——所有消費者下次讀取時自動拿到新 key。</p>
<p>Secrets Manager 支援自動輪替：設定一個 Lambda function 作為 rotation function，它負責建新 key → 更新 secret value → 停用舊 key 的全流程。</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_secretsmanager_secret&#34; &#34;deploy_key&#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;prod/deploy-bot/access-key&#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></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">resource</span> <span class="s2">&#34;aws_secretsmanager_secret_rotation&#34; &#34;deploy_key&#34;</span> {
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">  secret_id</span>           <span class="o">=</span> <span class="k">aws_secretsmanager_secret</span><span class="p">.</span><span class="k">deploy_key</span><span class="p">.</span><span class="k">id</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">  rotation_lambda_arn</span> <span class="o">=</span> <span class="k">aws_lambda_function</span><span class="p">.</span><span class="k">key_rotator</span><span class="p">.</span><span class="k">arn</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">rotation_rules</span> {
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="n">    automatically_after_days</span> <span class="o">=</span> <span class="m">90</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  }
</span></span><span class="line"><span class="ln">12</span><span class="cl">}</span></span></code></pre></div><p>自動輪替的前提是所有消費者都改成從 Secrets Manager 讀 key，而非從環境變數或設定檔。這個前提本身就是一次 migration——跟手動輪替的固定成本（盤點 + 更新 + 驗證）相比，migration 的一次性成本更高，但之後的每次輪替接近零成本。</p>
<p>判斷該不該投入自動化的依據是 key 的數量和輪替頻率。3 把 key、每季輪替一次，手動流程 2-3 小時可以完成，自動化的 ROI 不高。10 把以上、或合規要求 30 天輪替，手動已經吃掉固定的工程師時間，自動化的投入才有回報。</p>
<h2 id="key-age-監控">Key age 監控</h2>
<p>輪替做完不代表可以不管——如果沒有監控，三個月後又會回到「不知道有幾把超齡的 key」的狀態。</p>
<p>最低成本的監控是一條定期跑的 check，掃描所有 key 的年齡並在超過閾值時告警：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 列出所有超過 90 天的 active key（用 AWS Config 規則更可靠）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">aws configservice put-config-rule --config-rule <span class="s1">&#39;{
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s1">  &#34;ConfigRuleName&#34;: &#34;access-keys-rotated&#34;,
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="s1">  &#34;Source&#34;: {
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="s1">    &#34;Owner&#34;: &#34;AWS&#34;,
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="s1">    &#34;SourceIdentifier&#34;: &#34;ACCESS_KEYS_ROTATED&#34;
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="s1">  },
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="s1">  &#34;InputParameters&#34;: &#34;{\&#34;maxAccessKeyAge\&#34;:\&#34;90\&#34;}&#34;
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="s1">}&#39;</span></span></span></code></pre></div><p>AWS Config 的 <code>ACCESS_KEYS_ROTATED</code> managed rule 會持續掃描所有 IAM user 的 key age，超過設定天數的標記為 non-compliant。把 Config 的 non-compliant 事件接到 SNS → Slack 或 email，就有了持續的 key 超齡告警。</p>
<p>Prowler 也提供 key age 檢查（<code>prowler aws --checks access_key_1_rotated</code>），適合當一次性掃描工具。Config rule 適合持續監控。</p>
<p>管理層報告可以用 Config 的 compliance dashboard：compliant key 數 / 總 key 數 = key rotation 覆蓋率，這個百分比適合放進月報。</p>
<p>IAM Access Analyzer 的 unused access 功能（需啟用 analyzer）可以持續掃描帳號內未使用的 key 和 permission，跟 Config rule 互補——Config 看 key age，Access Analyzer 看 key 是否被使用。兩者搭配可以同時回答「這把 key 多久沒輪替」和「這把 key 有沒有在用」。</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>：access key 風險的系統性分析、OIDC 作為長期 key 的替代方案</li>
<li>→ <a href="/blog/infra/02-identity-credentials/team-access-management/" data-link-title="團隊權限分級與存取管理" data-link-desc="用 admin / operator / viewer 三級劃分團隊成員的雲端操作權限，設計臨時提權流程、定期 access review 節奏，以及 contractor 與外部 vendor 的存取邊界">團隊權限分級與存取管理</a>：離職時的 key 撤銷流程</li>
<li>→ <a href="/blog/infra/08-governance-habits/tagging-secrets/" data-link-title="Tagging 規範與 Secrets 不進 code" data-link-desc="tag 讓資源可盤點、可清理、可歸屬；密鑰存在專用服務裡而非 code 或 state，兩者都屬於 day-1 就該立的治理地基">治理好習慣</a>：secret 的儲存與引用紀律</li>
</ul>
]]></content:encoded></item></channel></rss>