<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Identity Security Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/tag/identity-security/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/tag/identity-security/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Mon, 06 Jul 2026 05:00:03 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Identity Security Archives - Linuxcent</title>
	<link>https://linuxcent.com/tag/identity-security/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>MFA Fatigue Attacks: How Uber Got Breached and How to Stop It</title>
		<link>https://linuxcent.com/mfa-fatigue-attack-uber-okta/</link>
					<comments>https://linuxcent.com/mfa-fatigue-attack-uber-okta/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Wed, 10 Jun 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Purple Team]]></category>
		<category><![CDATA[Identity Security]]></category>
		<category><![CDATA[MFA]]></category>
		<category><![CDATA[MFA Fatigue]]></category>
		<category><![CDATA[Okta]]></category>
		<category><![CDATA[OWASP]]></category>
		<category><![CDATA[Uber Breach]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1855</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span>MFA fatigue attacks exploit push-based MFA UX — not weak passwords. Anatomy of the Uber and Okta breaches and why hardware keys are the only structural fix.</p>
<p>The post <a href="https://linuxcent.com/mfa-fatigue-attack-uber-okta/">MFA Fatigue Attacks: How Uber Got Breached and How to Stop It</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><a href="/what-is-purple-team-security/">What is purple team security</a> → <a href="/owasp-top-10-cloud-infrastructure/">OWASP Top 10 mapped to cloud infrastructure</a> → <a href="/cloud-security-breaches-2020-2025/">Cloud security breaches 2020–2025</a> → <a href="/broken-access-control-aws/">Broken access control in AWS</a> → <strong>MFA fatigue attacks</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>An <strong>MFA fatigue attack</strong> exploits push-notification MFA (Duo, Okta Verify, Microsoft Authenticator) by flooding a user with push requests until they accept one — either out of exhaustion or after social engineering</li>
<li>Uber (September 2022): contractor credentials purchased on a criminal marketplace → repeated Duo push notifications → WhatsApp social engineering → push accepted → admin PAM credentials found on internal file share → full access to AWS, GCP, Slack, HackerOne</li>
<li>The attack works because push MFA creates a UX habit: &#8220;tap accept&#8221; is a trained response, not a decision</li>
<li>Detection: multiple MFA failures followed by a single success in a short window — Okta System Log, Azure AD Sign-in Log, AWS CloudTrail</li>
<li>The structural fix is replacing push MFA with phishing-resistant FIDO2 hardware keys — not security awareness training, not more push notifications, not &#8220;number matching&#8221; alone</li>
<li>Okta (October 2023): support system breach exposed session tokens → attackers bypassed MFA entirely by using stolen session context</li>
</ul>
<hr />
<blockquote>
<p><strong>OWASP Mapping:</strong> A07 Identification and Authentication Failures. The Uber breach is the defining infrastructure example. Okta demonstrates session token theft as a related A07 variant.</p>
</blockquote>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">┌─────────────────────────────────────────────────────────────────────┐
│                    MFA FATIGUE ATTACK ANATOMY                       │
│                                                                     │
│   STEP 1: OBTAIN CREDENTIALS                                        │
│   Attacker ──── phish / buy on market ──────&#x25b6; username + password  │
│                                                                     │
│   STEP 2: TRIGGER MFA FLOOD                                         │
│   Attacker ──── repeated login attempts ────&#x25b6; Push #1 → User: NO   │
│                                               Push #2 → User: NO   │
│                                               Push #3 → User: NO   │
│                                               Push #4 → User: ???   │
│                                                                     │
│   STEP 3: SOCIAL ENGINEERING LAYER                                  │
│   Attacker ──── &quot;Hi, I&#039;m from IT support.                           │
│                  Please accept the next push.&quot;                      │
│                                               Push #4 → User: YES  │
│                                                                     │
│   STEP 4: ACCESS                                                    │
│   Attacker ──── authenticated session ──────&#x25b6; Internal network      │
│                                               Enumerate shares      │
│                                               Find next credential  │
│                                                                     │
│   ═══════════════════════════════════════════════════════           │
│   WHY TRAINING DOESN&#039;T HELP:                                        │
│   Push MFA trains users to tap accept. The attacker exploits        │
│   the trained behavior. Education competes with habit.              │
│                                                                     │
│   WHY HARDWARE KEYS DO:                                             │
│   FIDO2 requires physical presence. WhatsApp message                │
│   cannot accept a hardware key challenge.                           │
└─────────────────────────────────────────────────────────────────────┘
</code></pre>
<p>An <strong>MFA fatigue attack</strong> is how you bypass multi-factor authentication without breaking encryption or stealing the MFA seed — you exploit the user&#8217;s psychology and the UX of push-notification systems. The attacker knows the password. The only thing standing between them and access is the user&#8217;s willingness to tap &#8220;deny&#8221; indefinitely.</p>
<hr />
<h2 id="the-uber-breach-anatomy-minute-by-minute">The Uber Breach: Anatomy Minute by Minute</h2>
<p>September 15, 2022. The attacker&#8217;s capabilities: a purchased credential set for an Uber contractor account, a phone number, and patience.</p>
<p><strong>The credential acquisition:</strong> Uber contractor credentials were available on criminal marketplaces. The attacker obtained a valid username and password for an Uber contractor&#8217;s Uber corporate account.</p>
<p><strong>The MFA flood:</strong></p>
<p>The contractor&#8217;s account had Duo push-based MFA enrolled. The attacker initiated login attempts repeatedly, triggering a sequence of Duo push notifications to the contractor&#8217;s phone. The contractor rejected three or four of them. At this point, most attacks would stop — but the attacker added a social engineering layer.</p>
<p><strong>The WhatsApp message:</strong></p>
<p>The attacker sent a WhatsApp message to the contractor&#8217;s number, claiming to be from Uber IT support:</p>
<blockquote>
<p>&#8220;Hi, this is the Uber IT support team. We&#8217;re seeing some issues with your account and need you to approve the next Duo notification to verify your identity.&#8221;</p>
</blockquote>
<p>The contractor accepted the next push notification.</p>
<p><strong>Post-authentication enumeration:</strong></p>
<p>With an authenticated session, the attacker accessed Uber&#8217;s internal network. On an internal network share accessible to contractors, they found a PowerShell script. In that script: hardcoded Thycotic admin credentials. Thycotic is a Privileged Access Management (PAM) system — it stores credentials for privileged accounts across an organization.</p>
<p><strong>The blast radius:</strong></p>
<p>With Thycotic admin access, the attacker retrieved credentials for:<br />
&#8211; AWS IAM accounts<br />
&#8211; GCP service accounts<br />
&#8211; Google Workspace admin<br />
&#8211; VMware vSphere<br />
&#8211; Slack workspace admin<br />
&#8211; HackerOne bug bounty program admin (including details of open security reports)</p>
<p>The entire Uber infrastructure was accessible from one contractor&#8217;s push notification acceptance.</p>
<p><strong>What Uber&#8217;s logs showed:</strong></p>
<pre><code class="" data-line="">2022-09-15T02:17:00Z  [Duo] user=contractor@uber.com  action=push_sent  result=rejected
2022-09-15T02:17:45Z  [Duo] user=contractor@uber.com  action=push_sent  result=rejected
2022-09-15T02:18:30Z  [Duo] user=contractor@uber.com  action=push_sent  result=rejected
2022-09-15T02:19:15Z  [Duo] user=contractor@uber.com  action=push_sent  result=rejected
2022-09-15T02:22:00Z  [Duo] user=contractor@uber.com  action=push_sent  result=approved
2022-09-15T02:22:05Z  [VPN] user=contractor@uber.com  connection=established  ip=&lt;attacker&gt;
</code></pre>
<p>Four rejections followed by one approval in a five-minute window. This is a detectable pattern — but only if someone is looking for it.</p>
<hr />
<h2 id="red-phase-simulating-mfa-fatigue">Red Phase: Simulating MFA Fatigue</h2>
<h3 id="what-the-attack-looks-like-in-tooling">What the Attack Looks Like in Tooling</h3>
<p>MFA fatigue attacks are conducted manually — an attacker with valid credentials and knowledge of which MFA system the target uses. No special tooling is required for the attack itself. What can be simulated:</p>
<p><strong>Option 1: Repeated legitimate login attempts (test account only)</strong></p>
<pre><code class="" data-line=""># DO NOT run against production accounts or accounts you don&#039;t own

# Using Okta API to authenticate (test environment only)
TEST_USERNAME=&quot;testuser@yourdomain.com&quot;
TEST_PASSWORD=&quot;TestPassword123!&quot;
OKTA_DOMAIN=&quot;your-org.okta.com&quot;

for i in {1..5}; do
  echo &quot;Attempt $i at $(date +%T)&quot;
  response=$(curl -s -X POST \
    &quot;https://${OKTA_DOMAIN}/api/v1/authn&quot; \
    -H &quot;Content-Type: application/json&quot; \
    -d &quot;{\&quot;username\&quot;: \&quot;${TEST_USERNAME}\&quot;, \&quot;password\&quot;: \&quot;${TEST_PASSWORD}\&quot;}&quot;)

  status=$(echo &quot;$response&quot; | jq -r &#039;.status&#039;)
  echo &quot;  Status: $status&quot;

  if [ &quot;$status&quot; = &quot;MFA_CHALLENGE&quot; ]; then
    state_token=$(echo &quot;$response&quot; | jq -r &#039;.stateToken&#039;)
    factor_id=$(echo &quot;$response&quot; | jq -r &#039;._embedded.factors[] | select(.factorType == &quot;push&quot;) | .id&#039;)
    echo &quot;  Factor ID: $factor_id (push notification triggered)&quot;

    # In a real attack, the attacker would poll for the MFA response:
    echo &quot;  Waiting 10 seconds for user to respond...&quot;
    sleep 10
  fi

  sleep 30  # Wait between attempts to avoid rate limiting
done
</code></pre>
<p><strong>Option 2: Tabletop exercise (no credentials required)</strong></p>
<p>For organizations that cannot run live credential tests, the tabletop simulation maps the attack against your specific IdP logs. Pull 30 days of authentication logs and look for the pattern:</p>
<pre><code class="" data-line=""># Okta System Log: find users with multiple MFA failures followed by success
curl -H &quot;Authorization: SSWS ${OKTA_API_TOKEN}&quot; \
  &quot;https://your-org.okta.com/api/v1/logs?filter=eventType+eq+\&quot;user.authentication.auth_via_mfa\&quot;&amp;limit=1000&quot; | \
  jq &#039;
    group_by(.actor.id) |
    map({
      user: .[0].actor.displayName,
      total: length,
      failures: [.[] | select(.outcome.result == &quot;FAILURE&quot;)] | length,
      successes: [.[] | select(.outcome.result == &quot;SUCCESS&quot;)] | length
    }) |
    sort_by(.failures) |
    reverse |
    .[0:20]
  &#039;
</code></pre>
<p>Users with high failure counts followed by eventual success are the fatigue attack pattern. Some will be legitimate (user locked themselves out, then called IT). The ones to investigate are those where the failure-to-success sequence happened in a short window (under 30 minutes) and from an unusual IP.</p>
<hr />
<h2 id="blue-phase-detection-across-identity-providers">Blue Phase: Detection Across Identity Providers</h2>
<h3 id="okta-push-notification-flood">Okta: Push Notification Flood</h3>
<pre><code class="" data-line=""># Okta System Log — detect repeated push failures from same user
# Query for: &gt;3 push failures within 10 minutes for same user
curl -H &quot;Authorization: SSWS ${OKTA_API_TOKEN}&quot; \
  &quot;https://your-org.okta.com/api/v1/logs?filter=eventType+eq+\&quot;user.authentication.auth_via_mfa\&quot;+and+outcome.result+eq+\&quot;FAILURE\&quot;&amp;since=$(date -u -d &#039;24 hours ago&#039; +%Y-%m-%dT%H:%M:%SZ)&quot; | \
  jq &#039;
    group_by(.actor.id, (.published[0:16])) |
    map(select(length &gt;= 3)) |
    map({
      user: .[0].actor.displayName,
      window: .[0].published[0:16],
      failure_count: length,
      ips: [.[].client.ipAddress] | unique
    })
  &#039;
</code></pre>
<h3 id="azure-ad-conditional-access-logs">Azure AD: Conditional Access Logs</h3>
<pre><code class="" data-line=""># Azure AD: MFA push denial flood detection (using Azure CLI)
az monitor activity-log list \
  --start-time &quot;$(date -u -d &#039;24 hours ago&#039; +%Y-%m-%dT%H:%M:%SZ)&quot; \
  --query &quot;[?contains(operationName.value, &#039;MFA&#039;)].{user:caller,time:eventTimestamp,result:status.value}&quot; \
  --output table
</code></pre>
<p>In Microsoft Sentinel, the detection rule for MFA fatigue:</p>
<pre><code class="" data-line="">// Azure AD MFA Fatigue Detection — Sentinel KQL
SigninLogs
| where TimeGenerated &gt; ago(24h)
| where AuthenticationRequirement == &quot;multiFactorAuthentication&quot;
| where ResultType != &quot;0&quot;  // Non-success
| summarize
    FailureCount = count(),
    SuccessCount = countif(ResultType == &quot;0&quot;),
    IPs = make_set(IPAddress),
    StartTime = min(TimeGenerated),
    EndTime = max(TimeGenerated)
    by UserPrincipalName, bin(TimeGenerated, 10m)
| where FailureCount &gt;= 3
| where SuccessCount &gt;= 1
| where datetime_diff(&#039;minute&#039;, EndTime, StartTime) &lt;= 30
| project UserPrincipalName, FailureCount, SuccessCount, IPs, StartTime, EndTime
| order by FailureCount desc
</code></pre>
<h3 id="aws-cloudtrail-console-session-after-mfa-flood">AWS CloudTrail: Console Session After MFA Flood</h3>
<p>If your organization uses AWS SSO (IAM Identity Center) with an external IdP, the CloudTrail event that matters is the console login event immediately following the MFA success:</p>
<pre><code class="" data-line=""># Find AWS console login events from unusual IPs
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=ConsoleLogin \
  --start-time &quot;$(date -d &#039;24 hours ago&#039; --iso-8601=seconds)&quot; \
  --query &#039;Events[].{Time:EventTime,User:Username,IP:CloudTrailEvent}&#039; \
  --output json | \
  jq &#039;.[] | {
    time: .Time,
    user: .User,
    ip: (.IP | fromjson | .sourceIPAddress),
    mfa: (.IP | fromjson | .additionalEventData.MFAUsed)
  }&#039;
</code></pre>
<h3 id="what-a-guardduty-alert-looks-like-for-this-attack">What a GuardDuty Alert Looks Like for This Attack</h3>
<p>GuardDuty does not generate a specific finding for MFA fatigue (it does not have visibility into IdP logs). What it may catch downstream:</p>
<ul>
<li><code class="" data-line="">UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B</code> — console login from unusual geographic location or Tor exit node</li>
<li><code class="" data-line="">Discovery:IAMUser/AnomalousBehavior</code> — if the attacker begins enumerating IAM after console access</li>
</ul>
<p>The gap: GuardDuty&#8217;s behavioral analysis is per-account. If the attacker logs in using valid credentials and MFA, GuardDuty may not flag the initial access — only downstream actions that deviate from baseline.</p>
<hr />
<h2 id="purple-phase-the-structural-fix">Purple Phase: The Structural Fix</h2>
<h3 id="fix-1-replace-push-mfa-with-fido2-hardware-keys-for-tier-0-accounts">Fix 1: Replace Push MFA with FIDO2 Hardware Keys (for Tier-0 Accounts)</h3>
<p>This is the only structural fix. MFA fatigue attacks work because push notifications can be approved by a human who is socially engineered. FIDO2 hardware keys (YubiKey, Google Titan, etc.) require physical possession of the key and a user gesture (touch). A WhatsApp message cannot substitute for physical key presence.</p>
<pre><code class="" data-line=""># Okta: Require hardware key MFA for admin accounts
# (done via Okta Admin Console → Security → Authentication Policies)
# CLI example using Okta API:

# Create a new authentication policy requiring hardware authenticator
curl -X POST \
  &quot;https://your-org.okta.com/api/v1/policies&quot; \
  -H &quot;Authorization: SSWS ${OKTA_API_TOKEN}&quot; \
  -H &quot;Content-Type: application/json&quot; \
  -d &#039;{
    &quot;name&quot;: &quot;Admin Hardware Key Policy&quot;,
    &quot;type&quot;: &quot;ACCESS_POLICY&quot;,
    &quot;status&quot;: &quot;ACTIVE&quot;,
    &quot;description&quot;: &quot;Requires FIDO2 hardware key for admin access&quot;
  }&#039;
</code></pre>
<p><strong>Phasing hardware keys across an organization:</strong></p>
<table>
<thead>
<tr>
<th>Tier</th>
<th>Examples</th>
<th>Timeline</th>
</tr>
</thead>
<tbody>
<tr>
<td>Tier 0 — immediate</td>
<td>Cloud admin, IAM admin, Okta admin, DNS admin</td>
<td>Week 1</td>
</tr>
<tr>
<td>Tier 1 — 30 days</td>
<td>All engineers with production access</td>
<td>Month 1</td>
</tr>
<tr>
<td>Tier 2 — 90 days</td>
<td>All employees with SSO access</td>
<td>Month 3</td>
</tr>
<tr>
<td>Contractors</td>
<td>Scope-limited access, enforce at boundary</td>
<td>Immediate</td>
</tr>
</tbody>
</table>
<h3 id="fix-2-number-matching-intermediate-mitigation">Fix 2: Number Matching (Intermediate Mitigation)</h3>
<p>If hardware keys cannot be deployed immediately, number matching significantly reduces MFA fatigue effectiveness. Instead of a simple &#8220;approve/deny&#8221; push, the user must match a number shown on the login screen to a number shown in the authenticator app. This breaks the fatigue pattern — the attacker cannot trigger an approval without the user actively entering the correct number.</p>
<pre><code class="" data-line=""># Duo: Enable number matching
# Duo Admin Console → Policies → Duo Push Number Matching: Required

# Microsoft Authenticator: Enable number matching
# Azure AD → Security → Authentication methods → Microsoft Authenticator
# Enable: &quot;Require number matching for push notifications&quot;

# Okta Verify: Enable TOTP-bound push
# Okta Admin → Security → Multifactor → Okta Verify → Enable &quot;Number Challenge&quot;
</code></pre>
<h3 id="fix-3-detect-and-block-automated-response-to-fatigue-pattern">Fix 3: Detect and Block — Automated Response to Fatigue Pattern</h3>
<pre><code class="" data-line="">#!/usr/bin/env python3
# Purple Team EP05 — MFA Fatigue Auto-Response
# Monitors Okta System Log; suspends user on fatigue pattern detection
# Run as a Lambda function or scheduled script in your SIEM pipeline

import boto3
import requests
import json
from datetime import datetime, timedelta

OKTA_DOMAIN = &quot;your-org.okta.com&quot;
OKTA_TOKEN = &quot;your-okta-api-token&quot;  # use Secrets Manager in production
SNS_TOPIC_ARN = &quot;arn:aws:sns:us-east-1:123456789012:security-alerts&quot;

def get_recent_mfa_events(hours=1):
    since = (datetime.utcnow() - timedelta(hours=hours)).strftime(&quot;%Y-%m-%dT%H:%M:%SZ&quot;)
    url = f&quot;https://{OKTA_DOMAIN}/api/v1/logs&quot;
    params = {
        &quot;filter&quot;: &#039;eventType eq &quot;user.authentication.auth_via_mfa&quot;&#039;,
        &quot;since&quot;: since,
        &quot;limit&quot;: 1000
    }
    headers = {&quot;Authorization&quot;: f&quot;SSWS {OKTA_TOKEN}&quot;}
    response = requests.get(url, params=params, headers=headers)
    return response.json()

def detect_fatigue_pattern(events, failure_threshold=3, window_minutes=10):
    user_events = {}
    for event in events:
        user_id = event[&quot;actor&quot;][&quot;id&quot;]
        user_name = event[&quot;actor&quot;][&quot;displayName&quot;]
        result = event[&quot;outcome&quot;][&quot;result&quot;]
        timestamp = event[&quot;published&quot;]

        if user_id not in user_events:
            user_events[user_id] = {&quot;name&quot;: user_name, &quot;events&quot;: []}
        user_events[user_id][&quot;events&quot;].append({&quot;result&quot;: result, &quot;time&quot;: timestamp})

    fatigue_users = []
    for user_id, data in user_events.items():
        events_sorted = sorted(data[&quot;events&quot;], key=lambda x: x[&quot;time&quot;])
        failures = [e for e in events_sorted if e[&quot;result&quot;] == &quot;FAILURE&quot;]

        if len(failures) &gt;= failure_threshold:
            # Check if a success followed the failures
            last_failure_time = failures[-1][&quot;time&quot;]
            successes_after = [
                e for e in events_sorted
                if e[&quot;result&quot;] == &quot;SUCCESS&quot; and e[&quot;time&quot;] &gt; last_failure_time
            ]
            if successes_after:
                fatigue_users.append({
                    &quot;user_id&quot;: user_id,
                    &quot;user_name&quot;: data[&quot;name&quot;],
                    &quot;failure_count&quot;: len(failures),
                    &quot;success_after_failures&quot;: True
                })

    return fatigue_users

def alert_security_team(fatigue_users):
    sns = boto3.client(&quot;sns&quot;)
    message = f&quot;MFA FATIGUE ALERT — {len(fatigue_users)} user(s) detected:\n&quot;
    for user in fatigue_users:
        message += f&quot;  - {user[&#039;user_name&#039;]}: {user[&#039;failure_count&#039;]} failures then success\n&quot;

    sns.publish(
        TopicArn=SNS_TOPIC_ARN,
        Subject=&quot;Purple Team: MFA Fatigue Attack Detected&quot;,
        Message=message
    )

def lambda_handler(event, context):
    events = get_recent_mfa_events(hours=1)
    fatigue_users = detect_fatigue_pattern(events)
    if fatigue_users:
        alert_security_team(fatigue_users)
    return {&quot;fatigue_users_detected&quot;: len(fatigue_users)}
</code></pre>
<h3 id="fix-4-privileged-access-workstations-and-session-recording">Fix 4: Privileged Access Workstations and Session Recording</h3>
<p>The Uber breach succeeded because the attacker found hardcoded credentials on a file share accessible to contractors. The downstream fix after identity:</p>
<pre><code class="" data-line=""># Ensure no scripts or configuration files contain credentials
# Run TruffleHog against your internal repositories and file shares
trufflehog filesystem /path/to/internal/share \
  --json \
  --include-detectors=all \
  2&gt;/dev/null | \
  jq &#039;{file: .SourceMetadata.Data.Filesystem.file, detector: .DetectorName, verified: .Verified}&#039;
</code></pre>
<hr />
<h2 id="run-this-in-your-own-environment-mfa-audit">Run This in Your Own Environment: MFA Audit</h2>
<pre><code class="" data-line="">#!/bin/bash
# Purple Team EP05 — MFA Coverage Audit
# Checks for push-MFA users who are A07 exposure without hardware key enrollment

echo &quot;=== AWS: Console Users Without MFA ===&quot;
aws iam generate-credential-report &gt; /dev/null 2&gt;&amp;1
sleep 5
aws iam get-credential-report --query &#039;Content&#039; --output text | base64 -d | \
  awk -F&#039;,&#039; &#039;NR&gt;1 &amp;&amp; $4==&quot;true&quot; &amp;&amp; $8==&quot;false&quot; {
    print &quot;  USER: &quot; $1 &quot; | Console: &quot; $4 &quot; | MFA: &quot; $8
  }&#039;

echo &quot;&quot;
echo &quot;=== AWS: IAM Users with Long-Lived Access Keys (rotation risk) ===&quot;
aws iam get-credential-report --query &#039;Content&#039; --output text | base64 -d | \
  awk -F&#039;,&#039; &#039;NR&gt;1 &amp;&amp; $9!=&quot;N/A&quot; {
    cmd = &quot;date -d &quot; $10 &quot; +%s&quot;
    cmd | getline key_date; close(cmd)
    now = systime()
    age_days = int((now - key_date) / 86400)
    if (age_days &gt; 90) print &quot;  USER: &quot; $1 &quot; | KEY AGE: &quot; age_days &quot; days&quot;
  }&#039;

echo &quot;&quot;
echo &quot;=== RECOMMENDATION ===&quot;
echo &quot;  - Any console user without MFA = immediate A07 exposure&quot;
echo &quot;  - For accounts with Okta/Azure AD: run IdP-specific audit above&quot;
echo &quot;  - Hardware FIDO2 keys required for all admin accounts&quot;
</code></pre>
<hr />
<h2 id="common-mistakes-when-responding-to-mfa-fatigue-risk"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Mistakes When Responding to MFA Fatigue Risk</h2>
<p><strong>Mandating security training as the primary response.</strong> The Uber contractor was experienced. Training did not fail — the attacker exploited a social engineering vector that training cannot structurally prevent. Hardware keys remove the social engineering surface entirely.</p>
<p><strong>Implementing &#8220;number matching&#8221; and considering MFA fatigue solved.</strong> Number matching makes fatigue attacks harder, not impossible. A sophisticated attacker can relay the number in real time via voice call (&#8220;what number do you see on your screen?&#8221;). It buys time; it does not eliminate the attack class.</p>
<p><strong>Requiring MFA for employees but not contractors.</strong> The Uber breach was a contractor account. Contractor access policies tend to have looser MFA requirements because contractors often resist corporate MDM on personal devices. The solution is to scope contractor access tightly and require hardware key MFA at the access boundary, not push MFA.</p>
<p><strong>Not monitoring for the failure-then-success pattern.</strong> The Okta System Log, Azure AD Sign-in Logs, and Duo Admin Panel all have the data to detect MFA fatigue in real time. Most organizations generate these logs but do not have detection rules for the pattern. The detection is straightforward; the investment is adding the rule to your SIEM.</p>
<p><strong>Forgetting session tokens.</strong> The Okta breach was not MFA fatigue — it was session token theft. An attacker who can steal a valid session token does not need to beat MFA at all. Session token lifetime, storage security, and re-authentication requirements for sensitive operations are separate controls that address this variant.</p>
<hr />
<h2 id="quick-reference">Quick Reference</h2>
<table>
<thead>
<tr>
<th>Attack Variant</th>
<th>Mechanism</th>
<th>Structural Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td>Push notification flood</td>
<td>Attacker initiates logins repeatedly until user accepts</td>
<td>FIDO2 hardware key MFA</td>
</tr>
<tr>
<td>Social engineering layer</td>
<td>Attacker contacts user claiming to be IT support</td>
<td>Hardware key (physical presence required)</td>
</tr>
<tr>
<td>Session token theft</td>
<td>Steal valid session without needing MFA at all</td>
<td>Short session lifetime + re-auth for sensitive ops</td>
</tr>
<tr>
<td>Number matching bypass</td>
<td>Relay number via voice call in real time</td>
<td>Hardware key (no relay possible)</td>
</tr>
<tr>
<td>SIM swap</td>
<td>Port victim&#8217;s phone number to attacker&#8217;s SIM; receive OTP</td>
<td>Hardware key (phone-independent)</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>An <strong>MFA fatigue attack</strong> exploits push notification UX — training users to tap &#8220;deny&#8221; competes with a trained habit of tapping &#8220;accept&#8221;; hardware keys eliminate the attack surface by requiring physical presence</li>
<li>The Uber breach (2022) was MFA fatigue + hardcoded credentials in a file share — two OWASP categories chained (A07 + A02)</li>
<li>Detection is straightforward: multiple MFA failures followed by a success in a short window — this pattern exists in every IdP&#8217;s logs; adding the detection rule is the work</li>
<li>Number matching is a meaningful intermediate mitigation; it is not a structural fix</li>
<li>Hardware FIDO2 keys are the structural fix — they require physical presence and are phishing-resistant by design</li>
<li>Tier-0 accounts (cloud admin, IAM admin, Okta admin) cannot wait for the phased rollout — hardware keys on day one</li>
<li>Session token theft (CircleCI, Okta support breach) is a related A07 variant: even perfect MFA is bypassed if a valid session token is exfiltrated</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP06 covers CI/CD secrets exposure — how pipeline breaches work, why storing credentials in environment variables is structurally dangerous, and how the CircleCI breach exposed secrets that teams thought were safely stored. The structural answer is OIDC workload identity (IAM EP07): short-lived credentials that cannot be exfiltrated because they don&#8217;t exist until the moment they&#8217;re needed.</p>
<p>Get EP06 in your inbox when it publishes → <a href="#subscribe">subscribe at linuxcent.com</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&amp;linkname=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fmfa-fatigue-attack-uber-okta%2F&#038;title=MFA%20Fatigue%20Attacks%3A%20How%20Uber%20Got%20Breached%20and%20How%20to%20Stop%20It" data-a2a-url="https://linuxcent.com/mfa-fatigue-attack-uber-okta/" data-a2a-title="MFA Fatigue Attacks: How Uber Got Breached and How to Stop It"></a></p><p>The post <a href="https://linuxcent.com/mfa-fatigue-attack-uber-okta/">MFA Fatigue Attacks: How Uber Got Breached and How to Stop It</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/mfa-fatigue-attack-uber-okta/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1855</post-id>	</item>
		<item>
		<title>Zero Trust Access in the Cloud: How the Evaluation Loop Actually Works</title>
		<link>https://linuxcent.com/zero-trust-iam/</link>
					<comments>https://linuxcent.com/zero-trust-iam/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 20 Apr 2026 17:06:46 +0000</pubDate>
				<category><![CDATA[Cloud IAM]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[Identity Security]]></category>
		<category><![CDATA[JIT Access]]></category>
		<category><![CDATA[Zero Trust]]></category>
		<category><![CDATA[Zero Trust Architecture]]></category>
		<category><![CDATA[Zero Trust IAM]]></category>
		<guid isPermaLink="false">https://linuxcent.com/zero-trust-iam/</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span>Zero Trust IAM architecture: eliminate implicit trust, enforce just-in-time least privilege, assume breach, and build continuous context-aware access verification.</p>
<p>The post <a href="https://linuxcent.com/zero-trust-iam/">Zero Trust Access in the Cloud: How the Evaluation Loop Actually Works</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 10</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<hr />
<p><a href="/what-is-cloud-iam/">What Is Cloud IAM</a> → <a href="/authentication-vs-authorization-iam/">Authentication vs Authorization</a> → <a href="/iam-roles-policies-permissions-explained/">IAM Roles vs Policies</a> → <a href="/aws-iam-deep-dive/">AWS IAM Deep Dive</a> → <a href="/gcp-iam-deep-dive/">GCP Resource Hierarchy IAM</a> → <a href="/azure-rbac-entra-id-guide/">Azure RBAC Scopes</a> → <a href="/workload-identity-oidc-service-accounts/">OIDC Workload Identity</a> → <a href="/cloud-iam-privilege-escalation/">AWS IAM Privilege Escalation</a> → <a href="/iam-least-privilege-audit/">AWS Least Privilege Audit</a> → <a href="/cloud-identity-federation-oidc-saml/">SAML vs OIDC Federation</a> → <a href="/kubernetes-rbac-cloud-iam/">Kubernetes RBAC and AWS IAM</a> → <strong>Zero Trust Access in the Cloud</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Zero Trust: trust nothing implicitly, verify everything explicitly, minimize blast radius by assuming you will be breached</li>
<li>Network location is not identity — VPN is authentication for the tunnel, not authorization for the resource</li>
<li>JIT privilege elevation removes standing admin access: engineers request elevation for a specific purpose, scoped to a specific duration</li>
<li>Device posture is an access signal — a compromised endpoint with valid credentials is still a threat; Conditional Access gates on device compliance</li>
<li>Continuous session validation re-evaluates signals throughout the session — device falls out of compliance, sessions revoke in minutes, not at expiry</li>
<li>The highest-ROI early moves: eliminate machine static credentials, enforce MFA on all human access, federate to a single IdP</li>
</ul>
<hr />
<h2 id="the-big-picture">The Big Picture</h2>
<pre><code class="" data-line="">  ZERO TRUST IAM — EVERY REQUEST EVALUATED INDEPENDENTLY

  API call arrives
         │
         ▼
  Identity verified? ──── No ────► DENY
         │
        Yes
         │
         ▼
  Device compliant? ───── No ────► DENY (or step-up MFA)
         │
        Yes
         │
         ▼
  Policy allows this  ─── No ────► DENY
  action on this ARN?
         │
        Yes
         │
         ▼
  Conditions met? ─────── No ────► DENY
  (time, IP, MFA age,              (e.g., outside business hours,
   risk score, session)             impossible travel detected)
         │
        Yes
         │
         ▼
       ALLOW ──────────────────────► LOG every decision (allow and deny)
         │
         └── Continuous re-evaluation:
             device state changes → revoke
             anomaly detected → revoke or step-up
             credential age → require re-auth
</code></pre>
<hr />
<h2 id="introduction">Introduction</h2>
<p>The perimeter model of network security made a bet: inside the network is trusted, outside is not. Lock down the perimeter tightly enough and you&#8217;re safe. VPN in, and you&#8217;re one of us.</p>
<p>I grew up professionally in that model. Firewalls, DMZs, trusted zones. The idea had intuitive appeal — you build walls, you control what crosses them. For a while it worked reasonably well.</p>
<p>Then I watched it fail, repeatedly, in ways that were predictable in hindsight. An engineer&#8217;s laptop gets compromised at a coffee shop. They VPN in. Now the attacker is &#8220;inside.&#8221; A contractor account gets phished. They have valid Active Directory credentials. They&#8217;re inside. A cloud service gets misconfigured and exposes a management interface. There&#8217;s no perimeter for that to be inside of.</p>
<p>The perimeter model failed not because the walls weren&#8217;t strong enough, but because the premise was wrong. There is no inside. There is no perimeter that reliably separates trusted from untrusted. In a world of remote work, cloud services, contractor access, and API integrations, the attack surface doesn&#8217;t respect network boundaries.</p>
<p>Zero Trust is the architecture built on a different premise: <strong>trust nothing implicitly. Verify everything explicitly. Minimize blast radius by assuming you will be breached.</strong></p>
<p>This isn&#8217;t a product you buy. It&#8217;s a set of principles applied to how you design, build, and operate your IAM. This episode is how those principles translate to concrete practices — building on everything we&#8217;ve covered in this series.</p>
<hr />
<h2 id="the-three-principles">The Three Principles</h2>
<h3 id="verify-explicitly">Verify Explicitly</h3>
<p>Every request must carry verifiable identity and context. Network location is not identity.</p>
<pre><code class="" data-line="">Old model: request from 10.0.0.0/8 → trusted, proceed
Zero Trust: request from 10.0.0.0/8 → still must present verifiable identity
                                       still must pass authorization check
                                       still must pass context evaluation
                                       then proceed (or deny)
</code></pre>
<p>In cloud IAM terms: every API call carries identity claims (IAM role ARN, federated identity, managed identity), and those claims are verified against policy on every single request. There&#8217;s no concept of &#8220;once authenticated, trusted until logout.&#8221; In cloud IAM, this already exists natively. Every API call is authenticated and authorized independently. The challenge is extending this model to internal services, internal APIs, and human access patterns.</p>
<p>Implementation in practice:<br />
&#8211; mTLS for service-to-service communication — both sides present certificates; identity is the certificate, not the network path<br />
&#8211; Bearer tokens on every internal API call — no session cookies, no &#8220;we&#8217;re on the same VPC so it&#8217;s fine&#8221;<br />
&#8211; Short-lived credentials everywhere — a compromised credential expires, not &#8220;after the session times out in 8 hours&#8221;</p>
<h3 id="use-least-privilege-just-in-time-just-enough">Use Least Privilege — Just-in-Time, Just-Enough</h3>
<p>No standing access to sensitive resources. Access granted when needed, for the minimum scope, for the minimum duration.</p>
<pre><code class="" data-line="">Old model: alice is in the DBA group → permanent access to all databases
Zero Trust: alice requests access to production DB →
            verified: alice&#039;s device is enrolled in MDM and compliant
            verified: alice has an open change ticket for this task
            verified: current time is within business hours
            granted: connection to this specific database, from alice&#039;s specific IP
                     for 2 hours, then revoked automatically
</code></pre>
<p>This is JIT access. It reduces the window where a compromised credential can cause damage. It requires a change in how engineers think about access: access is not a property you have, it&#8217;s something you request when you need it. The operational friction is a feature, not a bug. Justifying each elevated access request is what keeps the access model honest.</p>
<h3 id="assume-breach">Assume Breach</h3>
<p>Design systems as if the attacker is already inside. This drives different decisions:</p>
<ul>
<li>Micro-segmentation: one role per service, minimum permissions per role. If one service is compromised, it can&#8217;t pivot to everything else.</li>
<li>Log everything: every authorization decision, allow or deny. When you&#8217;re investigating an incident, you need to know what happened, not just that something happened.</li>
<li>Automate response: anomalous API call pattern → trigger automated credential revocation or session termination. Don&#8217;t wait for a human to notice.</li>
</ul>
<hr />
<h2 id="building-zero-trust-iam-block-by-block">Building Zero Trust IAM — Block by Block</h2>
<h3 id="block-1-strong-identity-foundation">Block 1: Strong Identity Foundation</h3>
<p>You can&#8217;t verify explicitly without strong authentication. The starting point:</p>
<pre><code class="" data-line=""># AWS: require MFA for all IAM operations — enforce via SCP across the org
{
  &quot;Effect&quot;: &quot;Deny&quot;,
  &quot;Action&quot;: &quot;*&quot;,
  &quot;Resource&quot;: &quot;*&quot;,
  &quot;Condition&quot;: {
    &quot;BoolIfExists&quot;: {
      &quot;aws:MultiFactorAuthPresent&quot;: &quot;false&quot;
    },
    &quot;StringNotLike&quot;: {
      &quot;aws:PrincipalArn&quot;: [
        &quot;arn:aws:iam::*:role/AWSServiceRole*&quot;,
        &quot;arn:aws:iam::*:role/OrganizationAccountAccessRole&quot;
      ]
    }
  }
}
</code></pre>
<pre><code class="" data-line=""># GCP: enforce OS Login for VM SSH (ties SSH access to Google identity, not SSH keys)
gcloud compute project-info add-metadata \
  --metadata enable-oslogin=TRUE

# This means: SSH to a VM requires your Google identity to have roles/compute.osLogin
# or roles/compute.osAdminLogin. No more managing ~/.authorized_keys files on instances.
</code></pre>
<p>For human access: hardware FIDO2 keys (YubiKey, Google Titan) rather than TOTP where possible. TOTP codes can be phished in real-time adversary-in-the-middle attacks. Hardware keys cannot — the cryptographic challenge-response is bound to the origin URL.</p>
<h3 id="block-2-device-posture-as-an-access-signal">Block 2: Device Posture as an Access Signal</h3>
<p>In a Zero Trust model, the identity of the user is necessary but not sufficient. The state of the device matters too — a compromised endpoint with valid credentials is still a threat.</p>
<pre><code class="" data-line=""># Azure Conditional Access: block access from non-compliant devices
# (configures in Entra ID Conditional Access portal)
conditions:
  clientAppTypes: [browser, mobileAppsAndDesktopClients]
  devices:
    deviceFilter:
      mode: exclude
      rule: &quot;device.isCompliant -eq True and device.trustType -eq &#039;AzureAD&#039;&quot;
grantControls:
  builtInControls: [compliantDevice]
</code></pre>
<pre><code class="" data-line=""># AWS Verified Access: identity + device posture for application access — no VPN
aws ec2 create-verified-access-instance \
  --description &quot;Zero Trust app access&quot;

# Attach identity trust provider (Okta OIDC)
aws ec2 create-verified-access-trust-provider \
  --trust-provider-type user \
  --user-trust-provider-type oidc \
  --oidc-options IssuerURL=https://company.okta.com,ClientId=...,ClientSecret=...,Scope=openid

# Attach device trust provider (Jamf, Intune, or CrowdStrike)
aws ec2 create-verified-access-trust-provider \
  --trust-provider-type device \
  --device-trust-provider-type jamf \
  --device-options TenantId=JAMF_TENANT_ID
</code></pre>
<p>AWS Verified Access allows users to reach internal applications by verifying both their identity (via OIDC) and their device health (via MDM) — without a VPN. The access gateway evaluates both signals on every connection, not just at login.</p>
<h3 id="block-3-just-in-time-privilege-elevation">Block 3: Just-in-Time Privilege Elevation</h3>
<p>No standing elevated access. Engineers are eligible for elevated roles; they activate them when needed.</p>
<pre><code class="" data-line=""># Azure PIM: engineer activates an eligible privileged role
az rest --method POST \
  --uri &quot;https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests&quot; \
  --body &#039;{
    &quot;action&quot;: &quot;selfActivate&quot;,
    &quot;principalId&quot;: &quot;USER_OBJECT_ID&quot;,
    &quot;roleDefinitionId&quot;: &quot;ROLE_DEF_ID&quot;,
    &quot;directoryScopeId&quot;: &quot;/&quot;,
    &quot;justification&quot;: &quot;Investigating security alert in tenant — incident ticket INC-2026-0411&quot;,
    &quot;scheduleInfo&quot;: {
      &quot;startDateTime&quot;: &quot;2026-04-11T09:00:00Z&quot;,
      &quot;expiration&quot;: {&quot;type&quot;: &quot;AfterDuration&quot;, &quot;duration&quot;: &quot;PT4H&quot;}
    }
  }&#039;
# Access activates, lasts 4 hours, then automatically removed
</code></pre>
<pre><code class="" data-line=""># AWS: temporary account assignment via Identity Center
# (typically triggered by ITSM workflow integration, not manual CLI)
aws sso-admin create-account-assignment \
  --instance-arn &quot;arn:aws:sso:::instance/ssoins-xxx&quot; \
  --target-id ACCOUNT_ID \
  --target-type AWS_ACCOUNT \
  --permission-set-arn &quot;arn:aws:sso:::permissionSet/ssoins-xxx/ps-yyy&quot; \
  --principal-type USER \
  --principal-id USER_ID

# Schedule deletion (using EventBridge + Lambda in a real deployment)
aws sso-admin delete-account-assignment \
  --instance-arn &quot;arn:aws:sso:::instance/ssoins-xxx&quot; \
  --target-id ACCOUNT_ID \
  --target-type AWS_ACCOUNT \
  --permission-set-arn &quot;arn:aws:sso:::permissionSet/ssoins-xxx/ps-yyy&quot; \
  --principal-type USER \
  --principal-id USER_ID
</code></pre>
<p>The operational change this requires: engineers stop thinking of access as something they hold permanently and start thinking of it as something they request for a specific purpose.</p>
<p>This feels like friction until you&#8217;re investigating an incident and you have a precise record of who activated what elevated access and why.</p>
<h3 id="block-4-continuous-session-validation">Block 4: Continuous Session Validation</h3>
<p>Traditional auth: verify once at login, trust the session until timeout.<br />
Zero Trust auth: re-evaluate access signals continuously throughout the session.</p>
<pre><code class="" data-line="">Session starts: identity verified + device compliant + IP in expected range
                → access granted

15 minutes later: impossible travel detected (IP changes to different country)
                  → step-up authentication required, or session terminated

Later: device compliance state changes (EDR detects malware)
       → all active sessions for this device revoked immediately
</code></pre>
<p>This requires integration between your identity platform and your device management / EDR tooling. Entra ID Conditional Access with Continuous Access Evaluation (CAE) implements this natively. When certain events occur — device compliance change, IP anomaly, token revocation — access tokens are invalidated within minutes rather than waiting for natural expiry.</p>
<pre><code class="" data-line="">// GCP: bind IAM access to an Access Context Manager access level
// Access level enforces device compliance — if device falls out of compliance,
// the access level is no longer satisfied and requests fail immediately
gcloud projects add-iam-policy-binding my-project \
  --member=&quot;user:alice@company.com&quot; \
  --role=&quot;roles/bigquery.admin&quot; \
  --condition=&quot;expression=request.auth.access_levels.exists(x, x == &#039;accessPolicies/POLICY_NUM/accessLevels/corporate_compliant_device&#039;),title=Compliant device required&quot;
</code></pre>
<h3 id="block-5-micro-segmented-permissions">Block 5: Micro-Segmented Permissions</h3>
<p>Every service has its own identity. Every identity has only what it needs. Compromise of one service cannot propagate to others.</p>
<pre><code class="" data-line=""># Terraform: IAM as code — each service gets a dedicated, scoped role
resource &quot;aws_iam_role&quot; &quot;order_processor&quot; {
  name                 = &quot;svc-order-processor&quot;
  permissions_boundary = aws_iam_policy.service_boundary.arn

  assume_role_policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [{
      Effect    = &quot;Allow&quot;
      Principal = { Service = &quot;lambda.amazonaws.com&quot; }
      Action    = &quot;sts:AssumeRole&quot;
    }]
  })
}

resource &quot;aws_iam_role_policy&quot; &quot;order_processor&quot; {
  name   = &quot;order-processor-policy&quot;
  role   = aws_iam_role.order_processor.id
  policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
        Effect   = &quot;Allow&quot;
        Action   = [&quot;sqs:ReceiveMessage&quot;, &quot;sqs:DeleteMessage&quot;, &quot;sqs:GetQueueAttributes&quot;]
        Resource = aws_sqs_queue.orders.arn
      },
      {
        Effect   = &quot;Allow&quot;
        Action   = [&quot;dynamodb:PutItem&quot;, &quot;dynamodb:GetItem&quot;, &quot;dynamodb:UpdateItem&quot;]
        Resource = aws_dynamodb_table.orders.arn
      }
    ]
  })
}
</code></pre>
<pre><code class="" data-line=""># Open Policy Agent: enforce IAM standards at the policy level
# Run this in CI/CD — fail the build if any policy statement has wildcard actions
package iam.policy

deny[msg] {
  input.Statement[i].Effect == &quot;Allow&quot;
  input.Statement[i].Action == &quot;*&quot;
  msg := sprintf(&quot;Statement %d has wildcard Action — not allowed&quot;, [i])
}

deny[msg] {
  input.Statement[i].Effect == &quot;Allow&quot;
  input.Statement[i].Resource == &quot;*&quot;
  endswith(input.Statement[i].Action, &quot;Delete&quot;)
  msg := sprintf(&quot;Statement %d allows Delete on all resources — requires specific ARN&quot;, [i])
}
</code></pre>
<h3 id="block-6-universal-audit-trail">Block 6: Universal Audit Trail</h3>
<p>Zero Trust without logging is just obscurity. Every authorization decision — allow and deny — must be logged, retained, and queryable.</p>
<pre><code class="" data-line=""># AWS: verify CloudTrail is comprehensive
aws cloudtrail get-trail-status --name management-trail
# Must have: LoggingEnabled=true, IsMultiRegionTrail=true, IncludeGlobalServiceEvents=true

# Verify no management events are excluded
aws cloudtrail get-event-selectors --trail-name management-trail \
  | jq &#039;.EventSelectors[] | {ReadWrite: .ReadWriteType, Mgmt: .IncludeManagementEvents}&#039;
# ReadWriteType should be &quot;All&quot;; IncludeManagementEvents should be true

# GCP: ensure Data Access audit logs are enabled for IAM
gcloud projects get-iam-policy my-project --format=json | jq &#039;.auditConfigs&#039;
# Should see auditLogConfigs for cloudresourcemanager.googleapis.com and iam.googleapis.com
# with both DATA_READ and DATA_WRITE enabled

# Azure: route Entra ID logs to Log Analytics for long-term retention and querying
az monitor diagnostic-settings create \
  --name entra-audit-to-la \
  --resource &quot;/tenants/TENANT_ID/providers/microsoft.aad/domains/company.com&quot; \
  --logs &#039;[{&quot;category&quot;:&quot;AuditLogs&quot;,&quot;enabled&quot;:true},{&quot;category&quot;:&quot;SignInLogs&quot;,&quot;enabled&quot;:true}]&#039; \
  --workspace /subscriptions/SUB_ID/resourceGroups/rg-monitoring/providers/Microsoft.OperationalInsights/workspaces/security-logs
</code></pre>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<p>Zero Trust IAM isn&#8217;t a framework itself — it&#8217;s a design philosophy. But it maps cleanly onto the controls that compliance frameworks are pushing organizations toward:</p>
<table>
<thead>
<tr>
<th>Framework</th>
<th>Reference</th>
<th>What It Covers Here</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP</td>
<td>Domain 5 — IAM</td>
<td>Zero Trust reframes IAM as continuous, context-aware verification rather than perimeter-based trust</td>
</tr>
<tr>
<td>CISSP</td>
<td>Domain 1 — Security &amp; Risk Management</td>
<td>Assume breach as a risk management posture; blast radius minimization through least privilege</td>
</tr>
<tr>
<td>CISSP</td>
<td>Domain 7 — Security Operations</td>
<td>Continuous monitoring, anomaly detection, and automated response are operational requirements of Zero Trust</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.15 Access control</td>
<td>Zero Trust access policy: verify explicitly, least privilege, assume breach</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>8.16 Monitoring activities</td>
<td>Continuous session validation and universal audit trail — all authorization decisions logged</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>8.20 Networks security</td>
<td>Micro-segmentation and mTLS replace implicit network trust with verified identity at every hop</td>
</tr>
<tr>
<td>ISO 27001:2022</td>
<td>5.23 Information security for cloud services</td>
<td>Zero Trust architecture applied to cloud IAM across AWS, GCP, and Azure</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.1</td>
<td>Zero Trust logical access controls — JIT, device posture, context-aware authorization</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC6.7</td>
<td>Continuous session validation and transmission controls across all system components</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC7.1</td>
<td>Threat detection through universal audit trails and anomaly-triggered automated response</td>
</tr>
<tr>
<td>SOC 2</td>
<td>CC7.2</td>
<td>Incident response — automated revocation and session termination on anomaly detection</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="zero-trust-maturity-where-to-start">Zero Trust Maturity — Where to Start</h2>
<p>In practice, most organizations think about Zero Trust as a destination — a large, multi-year program. The reality is it&#8217;s a direction. Any movement in that direction reduces risk.</p>
<table>
<thead>
<tr>
<th>Level</th>
<th>Where You Are</th>
<th>What to Build Next</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 — Initial</td>
<td>Some MFA; static credentials for machines; no centralized IdP</td>
<td>Eliminate machine static keys → workload identity</td>
</tr>
<tr>
<td>2 — Managed</td>
<td>Centralized IdP; SSO for most systems; some MFA enforcement</td>
<td>Close SSO gaps; enforce MFA everywhere; federate to cloud</td>
</tr>
<tr>
<td>3 — Defined</td>
<td>Least privilege being enforced; audit tooling in use; JIT for some privileged access</td>
<td>Expand JIT; policy-as-code in CI/CD; quarterly access reviews</td>
</tr>
<tr>
<td>4 — Contextual</td>
<td>Device posture in access decisions; conditional access policies</td>
<td>Continuous session evaluation; automated anomaly response</td>
</tr>
<tr>
<td>5 — Optimizing</td>
<td>Policy-as-code everywhere; automated right-sizing; anomaly-triggered revocation</td>
<td>Refine and maintain — Zero Trust is never &#8220;done&#8221;</td>
</tr>
</tbody>
</table>
<p>The jump from Level 1 to Level 3 delivers the most security value per unit of effort. Start there. Don&#8217;t defer least privilege enforcement while you build a sophisticated device posture integration.</p>
<hr />
<h2 id="the-practical-sequence">The Practical Sequence</h2>
<p>If you&#8217;re building Zero Trust IAM from where most organizations are, this is the order that maximizes early security value:</p>
<ol>
<li>
<p><strong>Inventory all identities</strong> — human and machine. You cannot secure what you can&#8217;t see. Build a complete picture before changing anything.</p>
</li>
<li>
<p><strong>Eliminate static credentials for machines</strong> — replace access keys and SA key files with workload identity. This is the highest-ROI change in most environments.</p>
</li>
<li>
<p><strong>Enforce MFA for all human access</strong> — especially cloud consoles, IdP admin, and VPN. Hardware keys for privileged accounts.</p>
</li>
<li>
<p><strong>Federate human identity</strong> — single IdP, SSO to cloud and major applications. Centralize the revocation path.</p>
</li>
<li>
<p><strong>Right-size IAM permissions</strong> — use last-accessed data and IAM Recommender to find and remove unused permissions. This is a continuous discipline, not a one-time clean-up.</p>
</li>
<li>
<p><strong>JIT for privileged access</strong> — Azure PIM, AWS Identity Center assignment automation, or equivalent for all elevated roles. No standing admin.</p>
</li>
<li>
<p><strong>IAM as code</strong> — all IAM changes via Terraform/Pulumi/CDK, reviewed in pull requests, validated by Access Analyzer or OPA in CI/CD, applied through automation.</p>
</li>
<li>
<p><strong>Continuous monitoring</strong> — alerts on IAM mutations, anomalous API call patterns, new cross-account trust relationships, new public resource exposures.</p>
</li>
<li>
<p><strong>Add context signals</strong> — Conditional Access policies incorporating device posture. Access Context Manager in GCP. AWS Verified Access for application access.</p>
</li>
<li>
<p><strong>Automated response</strong> — anomaly detected → automatic credential suspension or session termination. Close the window between detection and containment.</p>
</li>
</ol>
<hr />
<h2 id="core-curriculum-complete">Core Curriculum Complete</h2>
<p>These twelve episodes covered Cloud IAM from the question &#8220;what even is IAM?&#8221; to Zero Trust architecture:</p>
<table>
<thead>
<tr>
<th>Episode</th>
<th>Topic</th>
<th>The Core Lesson</th>
</tr>
</thead>
<tbody>
<tr>
<td>EP01</td>
<td>What is IAM?</td>
<td>Access management is deny-by-default; every grant is an explicit decision</td>
</tr>
<tr>
<td>EP02</td>
<td>AuthN vs AuthZ</td>
<td>Two separate gates; passing one doesn&#8217;t open the other</td>
</tr>
<tr>
<td>EP03</td>
<td>Roles, Policies, Permissions</td>
<td>Structure prevents drift; wildcards accumulate into exposure</td>
</tr>
<tr>
<td>EP04</td>
<td>AWS IAM Deep Dive</td>
<td>Trust policies and permission policies are both required; the evaluation chain has six layers</td>
</tr>
<tr>
<td>EP05</td>
<td>GCP IAM Deep Dive</td>
<td>Hierarchy inheritance is a feature that needs careful handling; service account keys are an antipattern</td>
</tr>
<tr>
<td>EP06</td>
<td>Azure RBAC and Entra ID</td>
<td>Two separate authorization planes; managed identities are the right model for workloads</td>
</tr>
<tr>
<td>EP07</td>
<td>Workload Identity</td>
<td>Static credentials for machines are solvable at the root; OIDC token exchange replaces them</td>
</tr>
<tr>
<td>EP08</td>
<td>IAM Attack Paths</td>
<td>The attack chain runs through IAM; <code class="" data-line="">iam:PassRole</code> and its equivalents are privilege escalation primitives</td>
</tr>
<tr>
<td>EP09</td>
<td>Least Privilege Auditing</td>
<td>5% utilization is the average; the 95% excess is attack surface — and it&#8217;s measurable</td>
</tr>
<tr>
<td>EP10</td>
<td>Federation, OIDC, SAML</td>
<td>The IdP is the trust anchor; everything downstream is bounded by its security</td>
</tr>
<tr>
<td>EP11</td>
<td>Kubernetes RBAC</td>
<td>Two separate IAM layers; both must be secured; <code class="" data-line="">cluster-admin</code> is the first thing to audit</td>
</tr>
<tr>
<td>EP12</td>
<td>Zero Trust IAM</td>
<td>Trust nothing implicitly; verify everything explicitly; minimize blast radius through least privilege at every layer</td>
</tr>
</tbody>
</table>
<p>IAM is not a feature you configure. It&#8217;s a practice you maintain. The organizations that operate with genuinely low cloud IAM risk don&#8217;t have fewer identities — they have better visibility into what those identities can do, and why, and what happened when something went wrong.</p>
<p>That&#8217;s the foundation this series has been building toward — but the curriculum doesn&#8217;t stop here. AWS, GCP, and Azure ship new services constantly, and every new service ships new IAM permissions. Scoping a policy correctly on day one is a different skill than auditing it after the fact — that&#8217;s where this series goes next.</p>
<hr />
<p>The full series is at <a href="/cloud-iam-series/">linuxcent.com/cloud-iam-series</a>. Subscribe to get new Cloud IAM episodes as new cloud permissions land — plus the eBPF series running in parallel, covering what&#8217;s actually running in kernel space when Cilium, Falco, and Tetragon do their work.</p>
<p>Subscribe → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&amp;linkname=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fzero-trust-iam%2F&#038;title=Zero%20Trust%20Access%20in%20the%20Cloud%3A%20How%20the%20Evaluation%20Loop%20Actually%20Works" data-a2a-url="https://linuxcent.com/zero-trust-iam/" data-a2a-title="Zero Trust Access in the Cloud: How the Evaluation Loop Actually Works"></a></p><p>The post <a href="https://linuxcent.com/zero-trust-iam/">Zero Trust Access in the Cloud: How the Evaluation Loop Actually Works</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/zero-trust-iam/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1516</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-22 13:00:48 by W3 Total Cache
-->