212 lines
7.0 KiB
Django/Jinja
212 lines
7.0 KiB
Django/Jinja
#!/bin/bash
|
|
# Info: Automated session key rotation for Polkadot validator
|
|
# Author: Provisioning System
|
|
|
|
set -e
|
|
|
|
POLKADOT_BIN="{{ polkadot_validator.bin_path }}"
|
|
CONFIG_PATH="{{ polkadot_validator.config_path }}"
|
|
SESSION_KEYS_FILE="{{ polkadot_validator.session_keys.keys_file | default('/var/lib/polkadot/session-keys') }}"
|
|
ROTATION_INTERVAL="{{ polkadot_validator.session_keys.rotation_interval | default(86400) }}"
|
|
AUTO_ROTATE="{{ polkadot_validator.session_keys.auto_rotate | default(false) | lower }}"
|
|
RUN_USER="{{ polkadot_validator.run_user.name }}"
|
|
|
|
LOCK_FILE="/var/run/polkadot-session-rotation.lock"
|
|
LOG_FILE="/var/log/polkadot/session-rotation.log"
|
|
|
|
# Logging function
|
|
log() {
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
|
|
}
|
|
|
|
# Check if rotation is needed
|
|
check_rotation_needed() {
|
|
if [ ! -f "$SESSION_KEYS_FILE" ]; then
|
|
log "No session keys found, rotation needed"
|
|
return 0
|
|
fi
|
|
|
|
CURRENT_TIME=$(date +%s)
|
|
FILE_TIME=$(stat -c %Y "$SESSION_KEYS_FILE" 2>/dev/null || echo "0")
|
|
TIME_DIFF=$((CURRENT_TIME - FILE_TIME))
|
|
|
|
if [ "$TIME_DIFF" -gt "$ROTATION_INTERVAL" ]; then
|
|
log "Session keys are $TIME_DIFF seconds old, rotation needed (interval: $ROTATION_INTERVAL)"
|
|
return 0
|
|
else
|
|
log "Session keys are $TIME_DIFF seconds old, no rotation needed"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Perform key rotation
|
|
rotate_keys() {
|
|
log "Starting session key rotation..."
|
|
|
|
# Create lock file
|
|
if [ -f "$LOCK_FILE" ]; then
|
|
log "Rotation already in progress (lock file exists)"
|
|
return 1
|
|
fi
|
|
|
|
echo $$ > "$LOCK_FILE"
|
|
trap 'rm -f "$LOCK_FILE"' EXIT
|
|
|
|
# Backup current keys
|
|
if [ -f "$SESSION_KEYS_FILE" ]; then
|
|
BACKUP_FILE="$SESSION_KEYS_FILE.backup.$(date +%Y%m%d_%H%M%S)"
|
|
cp "$SESSION_KEYS_FILE" "$BACKUP_FILE"
|
|
log "Current keys backed up to: $BACKUP_FILE"
|
|
fi
|
|
|
|
# Generate new keys
|
|
log "Generating new session keys..."
|
|
RESULT=$(curl -s -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "author_rotateKeys", "params":[]}' http://localhost:9933 | jq -r '.result' 2>/dev/null || echo "")
|
|
|
|
if [ -n "$RESULT" ] && [ "$RESULT" != "null" ]; then
|
|
echo "$RESULT" > "$SESSION_KEYS_FILE"
|
|
chown "$RUN_USER:$RUN_USER" "$SESSION_KEYS_FILE"
|
|
chmod 600 "$SESSION_KEYS_FILE"
|
|
log "New session keys generated: $RESULT"
|
|
|
|
# Verify new keys
|
|
VERIFY_RESULT=$(curl -s -H "Content-Type: application/json" -d "{\"id\":1, \"jsonrpc\":\"2.0\", \"method\": \"author_hasSessionKeys\", \"params\":[\"$RESULT\"]}" http://localhost:9933 | jq -r '.result' 2>/dev/null || echo "false")
|
|
|
|
if [ "$VERIFY_RESULT" = "true" ]; then
|
|
log "✅ New session keys verified successfully"
|
|
|
|
# Send notification (if configured)
|
|
send_notification "Session keys rotated successfully" "$RESULT"
|
|
|
|
return 0
|
|
else
|
|
log "❌ Failed to verify new session keys"
|
|
return 1
|
|
fi
|
|
else
|
|
log "❌ Failed to generate new session keys"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Send notification
|
|
send_notification() {
|
|
local message="$1"
|
|
local keys="$2"
|
|
|
|
# Log the notification
|
|
log "NOTIFICATION: $message"
|
|
|
|
# Send to syslog
|
|
logger -t polkadot-validator "$message"
|
|
|
|
# Additional notification methods can be added here
|
|
# Examples: email, Slack, Discord, etc.
|
|
|
|
# Example webhook notification (uncomment and configure)
|
|
# if [ -n "$WEBHOOK_URL" ]; then
|
|
# curl -s -X POST "$WEBHOOK_URL" \
|
|
# -H "Content-Type: application/json" \
|
|
# -d "{\"text\":\"Polkadot Validator: $message\", \"keys\":\"$keys\"}" \
|
|
# || log "Failed to send webhook notification"
|
|
# fi
|
|
}
|
|
|
|
# Check validator health
|
|
check_validator_health() {
|
|
log "Checking validator health..."
|
|
|
|
# Check if node is running
|
|
if ! systemctl is-active --quiet polkadot-validator; then
|
|
log "❌ Validator service is not running"
|
|
return 1
|
|
fi
|
|
|
|
# Check node health via RPC
|
|
HEALTH=$(curl -s -H "Content-Type: application/json" -d '{"id":1, "jsonrpc":"2.0", "method": "system_health", "params":[]}' http://localhost:9933 | jq -r '.result' 2>/dev/null)
|
|
|
|
if [ -n "$HEALTH" ]; then
|
|
IS_SYNCING=$(echo "$HEALTH" | jq -r '.isSyncing' 2>/dev/null || echo "true")
|
|
PEERS=$(echo "$HEALTH" | jq -r '.peers' 2>/dev/null || echo "0")
|
|
|
|
if [ "$IS_SYNCING" = "false" ] && [ "$PEERS" -gt 0 ]; then
|
|
log "✅ Validator is healthy (synced, $PEERS peers)"
|
|
return 0
|
|
else
|
|
log "⚠️ Validator may have issues (syncing: $IS_SYNCING, peers: $PEERS)"
|
|
return 1
|
|
fi
|
|
else
|
|
log "❌ Cannot check validator health (RPC not responding)"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Main execution
|
|
case "${1:-check}" in
|
|
"rotate")
|
|
log "Manual session key rotation requested"
|
|
if check_validator_health; then
|
|
rotate_keys
|
|
else
|
|
log "Skipping rotation due to validator health issues"
|
|
exit 1
|
|
fi
|
|
;;
|
|
"check")
|
|
if [ "$AUTO_ROTATE" = "true" ]; then
|
|
log "Checking if automatic rotation is needed"
|
|
if check_rotation_needed && check_validator_health; then
|
|
rotate_keys
|
|
fi
|
|
else
|
|
log "Automatic rotation is disabled"
|
|
fi
|
|
;;
|
|
"force")
|
|
log "Forced session key rotation requested"
|
|
rotate_keys
|
|
;;
|
|
"health")
|
|
check_validator_health
|
|
;;
|
|
"status")
|
|
log "Session key rotation status:"
|
|
|
|
if [ -f "$SESSION_KEYS_FILE" ]; then
|
|
CURRENT_TIME=$(date +%s)
|
|
FILE_TIME=$(stat -c %Y "$SESSION_KEYS_FILE" 2>/dev/null || echo "0")
|
|
TIME_DIFF=$((CURRENT_TIME - FILE_TIME))
|
|
HOURS_OLD=$((TIME_DIFF / 3600))
|
|
|
|
log "Current keys are $HOURS_OLD hours old"
|
|
log "Rotation interval: $((ROTATION_INTERVAL / 3600)) hours"
|
|
log "Auto rotation: $AUTO_ROTATE"
|
|
|
|
if [ -f "$LOCK_FILE" ]; then
|
|
log "Rotation in progress (PID: $(cat "$LOCK_FILE"))"
|
|
else
|
|
log "No rotation in progress"
|
|
fi
|
|
else
|
|
log "No session keys found"
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {check|rotate|force|health|status}"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " check Check if rotation is needed and perform if auto-rotation enabled"
|
|
echo " rotate Perform rotation if health checks pass"
|
|
echo " force Force rotation regardless of timing"
|
|
echo " health Check validator health"
|
|
echo " status Show rotation status"
|
|
echo ""
|
|
echo "Configuration:"
|
|
echo " Auto rotation: $AUTO_ROTATE"
|
|
echo " Rotation interval: $((ROTATION_INTERVAL / 3600)) hours"
|
|
echo " Session keys file: $SESSION_KEYS_FILE"
|
|
echo " Log file: $LOG_FILE"
|
|
exit 1
|
|
;;
|
|
esac |