security fixes
This commit is contained in:
parent
6ab1333b41
commit
4364d81416
3 changed files with 108 additions and 12 deletions
21
check_prod_time.sh
Executable file
21
check_prod_time.sh
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
# Quick script to check production server time via Elixir remote console
|
||||
|
||||
echo "=== Checking Production Server Time ==="
|
||||
echo ""
|
||||
echo "Running remote Elixir command..."
|
||||
echo ""
|
||||
|
||||
kubectl exec -it -n towerops deployment/towerops -- bin/towerops rpc "
|
||||
IO.puts(\"System.system_time(:second): #{System.system_time(:second)}\")
|
||||
IO.puts(\"DateTime.utc_now(): #{DateTime.utc_now()}\")
|
||||
IO.puts(\"\")
|
||||
IO.puts(\"Expected UTC now: #{DateTime.from_unix!(System.system_time(:second))}\")
|
||||
"
|
||||
|
||||
echo ""
|
||||
echo "=== Current UTC Time (from local machine) ==="
|
||||
date -u
|
||||
echo ""
|
||||
echo "Compare the times above. If they differ by more than a few seconds,"
|
||||
echo "the production server's clock is out of sync."
|
||||
52
diagnose_totp_secret.sh
Executable file
52
diagnose_totp_secret.sh
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
#!/bin/bash
|
||||
# Diagnose TOTP secret for a specific user
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Usage: ./diagnose_totp_secret.sh <user_email>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_EMAIL="$1"
|
||||
|
||||
echo "=== TOTP Secret Diagnostic ==="
|
||||
echo "User: $USER_EMAIL"
|
||||
echo ""
|
||||
|
||||
kubectl exec -n towerops deployment/towerops -- bin/towerops rpc "
|
||||
alias Towerops.Accounts
|
||||
require Logger
|
||||
|
||||
user = Accounts.get_user_by_email(\"$USER_EMAIL\")
|
||||
|
||||
if is_nil(user) do
|
||||
IO.puts(\"❌ User not found\")
|
||||
else
|
||||
IO.puts(\"✓ User found: #{user.email}\")
|
||||
|
||||
if is_nil(user.totp_secret) do
|
||||
IO.puts(\"❌ TOTP not enabled for this user\")
|
||||
else
|
||||
IO.puts(\"✓ TOTP enabled\")
|
||||
IO.puts(\"Secret length: #{byte_size(user.totp_secret)} bytes\")
|
||||
|
||||
# Check if secret is valid Base32
|
||||
try do
|
||||
# NimbleTOTP expects the secret to be base32-encoded
|
||||
# Let's generate a code to verify the secret is valid
|
||||
current_time = System.system_time(:second)
|
||||
code = NimbleTOTP.verification_code(user.totp_secret, time: current_time)
|
||||
|
||||
IO.puts(\"✓ Secret is valid (can generate codes)\")
|
||||
IO.puts(\"\")
|
||||
IO.puts(\"Current expected codes:\")
|
||||
IO.puts(\" Current: #{code}\")
|
||||
IO.puts(\" Prev (-30s): #{NimbleTOTP.verification_code(user.totp_secret, time: current_time - 30)}\")
|
||||
IO.puts(\" Next (+30s): #{NimbleTOTP.verification_code(user.totp_secret, time: current_time + 30)}\")
|
||||
IO.puts(\"\")
|
||||
IO.puts(\"Server time: #{DateTime.from_unix!(current_time)}\")
|
||||
rescue
|
||||
e -> IO.puts(\"❌ Secret is INVALID: #{inspect(e)}\")
|
||||
end
|
||||
end
|
||||
end
|
||||
"
|
||||
|
|
@ -16,6 +16,8 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
|
|||
if Accounts.totp_enabled?(user) do
|
||||
{:ok, redirect(socket, to: ~p"/devices")}
|
||||
else
|
||||
# Generate secret once and store in process state
|
||||
# If user refreshes, they'll get a NEW secret - they must scan the LATEST QR code
|
||||
secret = Accounts.generate_totp_secret()
|
||||
qr_code = Accounts.generate_totp_qr_code(user, secret)
|
||||
|
||||
|
|
@ -196,18 +198,39 @@ defmodule ToweropsWeb.AccountLive.TotpEnrollment do
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<div class="flex">
|
||||
<.icon
|
||||
name="hero-information-circle"
|
||||
class="h-5 w-5 text-blue-600 dark:text-blue-400 mt-0.5"
|
||||
/>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-blue-900 dark:text-blue-100">
|
||||
<strong>Keep your device safe:</strong>
|
||||
You'll need to enter a code from your authenticator app each time you log in.
|
||||
Make sure to keep your phone secure and backed up.
|
||||
</p>
|
||||
<div class="mt-8 space-y-4">
|
||||
<div class="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-800">
|
||||
<div class="flex">
|
||||
<.icon
|
||||
name="hero-exclamation-triangle"
|
||||
class="h-5 w-5 text-yellow-600 dark:text-yellow-400 mt-0.5 flex-shrink-0"
|
||||
/>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-yellow-900 dark:text-yellow-100 font-semibold">
|
||||
Important: Do not refresh this page!
|
||||
</p>
|
||||
<p class="text-sm text-yellow-900 dark:text-yellow-100 mt-1">
|
||||
Each time you refresh, a new QR code is generated. You must scan the current QR code
|
||||
and enter the verification code immediately without refreshing.
|
||||
If you refresh by accident, you must scan the NEW QR code again.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800">
|
||||
<div class="flex">
|
||||
<.icon
|
||||
name="hero-information-circle"
|
||||
class="h-5 w-5 text-blue-600 dark:text-blue-400 mt-0.5 flex-shrink-0"
|
||||
/>
|
||||
<div class="ml-3">
|
||||
<p class="text-sm text-blue-900 dark:text-blue-100">
|
||||
<strong>Keep your device safe:</strong>
|
||||
You'll need to enter a code from your authenticator app each time you log in.
|
||||
Make sure to keep your phone secure and backed up.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue