+101
scripts/dpop-monitor.sh
+101
scripts/dpop-monitor.sh
···
···
1
+
#!/bin/bash
2
+
# Monitor PDS logs for DPoP JWTs and compare iat timestamps
3
+
# Usage: ./dpop-monitor.sh [pod-name]
4
+
5
+
POD="${1:-atproto-pds-6d5c45457d-wcmhc}"
6
+
7
+
echo "Monitoring DPoP JWTs from pod: $POD"
8
+
echo "Press Ctrl+C to stop"
9
+
echo "-------------------------------------------"
10
+
11
+
kubectl logs -f "$POD" 2>/dev/null | while read -r line; do
12
+
# Extract DPoP JWT from the line
13
+
dpop=$(echo "$line" | grep -oP '"dpop":"[^"]+' | sed 's/"dpop":"//')
14
+
15
+
if [ -n "$dpop" ]; then
16
+
# Extract log timestamp (milliseconds)
17
+
log_time_ms=$(echo "$line" | grep -oP '"time":\d+' | grep -oP '\d+')
18
+
19
+
# Extract URL
20
+
url=$(echo "$line" | grep -oP '"url":"[^"]+' | sed 's/"url":"//')
21
+
22
+
# Extract status code
23
+
status=$(echo "$line" | grep -oP '"statusCode":\d+' | grep -oP '\d+')
24
+
25
+
# Extract client IP (cf-connecting-ip)
26
+
client_ip=$(echo "$line" | grep -oP '"cf-connecting-ip":"[^"]+' | sed 's/"cf-connecting-ip":"//')
27
+
28
+
# Extract user-agent to identify the source
29
+
user_agent=$(echo "$line" | grep -oP '"user-agent":"[^"]+' | sed 's/"user-agent":"//')
30
+
31
+
# Extract referer (often contains the source app)
32
+
referer=$(echo "$line" | grep -oP '"referer":"[^"]+' | sed 's/"referer":"//' | grep -oP 'https://[^/]+' | sed 's|https://||')
33
+
34
+
# Decode JWT payload (second part between dots)
35
+
payload=$(echo "$dpop" | cut -d. -f2)
36
+
37
+
# Add padding if needed for base64
38
+
padding=$((4 - ${#payload} % 4))
39
+
if [ $padding -ne 4 ]; then
40
+
payload="${payload}$(printf '=%.0s' $(seq 1 $padding))"
41
+
fi
42
+
43
+
# Decode and extract iat
44
+
decoded=$(echo "$payload" | base64 -d 2>/dev/null)
45
+
iat=$(echo "$decoded" | grep -oP '"iat":\d+' | grep -oP '\d+')
46
+
exp=$(echo "$decoded" | grep -oP '"exp":\d+' | grep -oP '\d+')
47
+
htu=$(echo "$decoded" | grep -oP '"htu":"[^"]+' | sed 's/"htu":"//')
48
+
49
+
if [ -n "$iat" ] && [ -n "$log_time_ms" ]; then
50
+
# Convert log time to seconds
51
+
log_time_s=$((log_time_ms / 1000))
52
+
53
+
# Calculate difference (positive = token from future, negative = token from past)
54
+
diff=$((iat - log_time_s))
55
+
56
+
# Determine source - prefer referer, then htu domain, then user-agent
57
+
if [ -n "$referer" ]; then
58
+
source="$referer"
59
+
else
60
+
# Extract domain from htu (the target of the DPoP request)
61
+
htu_domain=$(echo "$htu" | grep -oP 'https://[^/]+' | sed 's|https://||')
62
+
63
+
# For server-to-server calls, try to identify by known IPs
64
+
case "$client_ip" in
65
+
152.44.36.124) source="atcr.io" ;;
66
+
2a04:3541:8000:1000:*) source="tangled.org" ;;
67
+
*)
68
+
if echo "$user_agent" | grep -q "indigo-sdk"; then
69
+
source="indigo-sdk"
70
+
elif echo "$user_agent" | grep -q "Go-http-client"; then
71
+
source="Go-app"
72
+
else
73
+
source="${user_agent:0:30}"
74
+
fi
75
+
source="$source ($client_ip)"
76
+
;;
77
+
esac
78
+
fi
79
+
80
+
# Color coding
81
+
if [ $diff -gt 0 ]; then
82
+
color="\033[31m" # Red - future token (problem!)
83
+
status_text="FUTURE"
84
+
elif [ $diff -lt -5 ]; then
85
+
color="\033[33m" # Yellow - old token
86
+
status_text="OLD"
87
+
else
88
+
color="\033[32m" # Green - ok
89
+
status_text="OK"
90
+
fi
91
+
reset="\033[0m"
92
+
93
+
echo ""
94
+
echo -e "${color}[$status_text]${reset} Diff: ${diff}s | Source: $source | Status: $status"
95
+
echo " iat (token): $iat ($(date -d @$iat -u '+%H:%M:%S UTC'))"
96
+
echo " PDS received: $log_time_s ($(date -d @$log_time_s -u '+%H:%M:%S UTC'))"
97
+
echo " URL: $url"
98
+
[ -n "$client_ip" ] && echo " Client IP: $client_ip"
99
+
fi
100
+
fi
101
+
done