diff --git a/scripts/dpop-monitor.sh b/scripts/dpop-monitor.sh new file mode 100755 index 0000000..6f768d9 --- /dev/null +++ b/scripts/dpop-monitor.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# Monitor PDS logs for DPoP JWTs and compare iat timestamps +# Usage: ./dpop-monitor.sh [pod-name] + +POD="${1:-atproto-pds-6d5c45457d-wcmhc}" + +echo "Monitoring DPoP JWTs from pod: $POD" +echo "Press Ctrl+C to stop" +echo "-------------------------------------------" + +kubectl logs -f "$POD" 2>/dev/null | while read -r line; do + # Extract DPoP JWT from the line + dpop=$(echo "$line" | grep -oP '"dpop":"[^"]+' | sed 's/"dpop":"//') + + if [ -n "$dpop" ]; then + # Extract log timestamp (milliseconds) + log_time_ms=$(echo "$line" | grep -oP '"time":\d+' | grep -oP '\d+') + + # Extract URL + url=$(echo "$line" | grep -oP '"url":"[^"]+' | sed 's/"url":"//') + + # Extract status code + status=$(echo "$line" | grep -oP '"statusCode":\d+' | grep -oP '\d+') + + # Extract client IP (cf-connecting-ip) + client_ip=$(echo "$line" | grep -oP '"cf-connecting-ip":"[^"]+' | sed 's/"cf-connecting-ip":"//') + + # Extract user-agent to identify the source + user_agent=$(echo "$line" | grep -oP '"user-agent":"[^"]+' | sed 's/"user-agent":"//') + + # Extract referer (often contains the source app) + referer=$(echo "$line" | grep -oP '"referer":"[^"]+' | sed 's/"referer":"//' | grep -oP 'https://[^/]+' | sed 's|https://||') + + # Decode JWT payload (second part between dots) + payload=$(echo "$dpop" | cut -d. -f2) + + # Add padding if needed for base64 + padding=$((4 - ${#payload} % 4)) + if [ $padding -ne 4 ]; then + payload="${payload}$(printf '=%.0s' $(seq 1 $padding))" + fi + + # Decode and extract iat + decoded=$(echo "$payload" | base64 -d 2>/dev/null) + iat=$(echo "$decoded" | grep -oP '"iat":\d+' | grep -oP '\d+') + exp=$(echo "$decoded" | grep -oP '"exp":\d+' | grep -oP '\d+') + htu=$(echo "$decoded" | grep -oP '"htu":"[^"]+' | sed 's/"htu":"//') + + if [ -n "$iat" ] && [ -n "$log_time_ms" ]; then + # Convert log time to seconds + log_time_s=$((log_time_ms / 1000)) + + # Calculate difference (positive = token from future, negative = token from past) + diff=$((iat - log_time_s)) + + # Determine source - prefer referer, then htu domain, then user-agent + if [ -n "$referer" ]; then + source="$referer" + else + # Extract domain from htu (the target of the DPoP request) + htu_domain=$(echo "$htu" | grep -oP 'https://[^/]+' | sed 's|https://||') + + # For server-to-server calls, try to identify by known IPs + case "$client_ip" in + 152.44.36.124) source="atcr.io" ;; + 2a04:3541:8000:1000:*) source="tangled.org" ;; + *) + if echo "$user_agent" | grep -q "indigo-sdk"; then + source="indigo-sdk" + elif echo "$user_agent" | grep -q "Go-http-client"; then + source="Go-app" + else + source="${user_agent:0:30}" + fi + source="$source ($client_ip)" + ;; + esac + fi + + # Color coding + if [ $diff -gt 0 ]; then + color="\033[31m" # Red - future token (problem!) + status_text="FUTURE" + elif [ $diff -lt -5 ]; then + color="\033[33m" # Yellow - old token + status_text="OLD" + else + color="\033[32m" # Green - ok + status_text="OK" + fi + reset="\033[0m" + + echo "" + echo -e "${color}[$status_text]${reset} Diff: ${diff}s | Source: $source | Status: $status" + echo " iat (token): $iat ($(date -d @$iat -u '+%H:%M:%S UTC'))" + echo " PDS received: $log_time_s ($(date -d @$log_time_s -u '+%H:%M:%S UTC'))" + echo " URL: $url" + [ -n "$client_ip" ] && echo " Client IP: $client_ip" + fi + fi +done