provisioning/taskservs/polkadot/validator/default/session-rotation.sh.j2

212 lines
7.0 KiB
Plaintext
Raw Normal View History

#!/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