#!/bin/bash # # NODE1 API Key Management # Version: 1.0 # Last Updated: 2026-01-26 # # Usage: # ./api-keys.sh create [--admin] # ./api-keys.sh revoke # ./api-keys.sh list # ./api-keys.sh rotate-health # ./api-keys.sh verify # set -e # Paths KEYS_DIR="/opt/microdao-daarion/secrets" KEYS_FILE="$KEYS_DIR/api-keys.conf" HEALTH_TOKEN_FILE="$KEYS_DIR/health-token.conf" NGINX_KEYS_MAP="$KEYS_DIR/nginx-api-keys.conf" LOG_FILE="/var/log/microdao/api-keys.log" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # Initialize directories init_dirs() { mkdir -p "$KEYS_DIR" mkdir -p "$(dirname $LOG_FILE)" chmod 700 "$KEYS_DIR" touch "$KEYS_FILE" "$HEALTH_TOKEN_FILE" "$NGINX_KEYS_MAP" chmod 600 "$KEYS_FILE" "$HEALTH_TOKEN_FILE" "$NGINX_KEYS_MAP" } # Log action (without exposing secrets) log_action() { local action="$1" local key_id="$2" local details="$3" echo "$(date '+%Y-%m-%d %H:%M:%S') | $action | key_id=$key_id | $details" >> "$LOG_FILE" } # Generate random key generate_key() { openssl rand -hex 32 } # Generate key ID generate_key_id() { local name="$1" local timestamp=$(date +%Y%m%d%H%M%S) local random=$(openssl rand -hex 4) echo "kid_${name}_${timestamp}_${random}" } # Create new API key create_key() { local name="$1" local is_admin="${2:-false}" if [ -z "$name" ]; then echo -e "${RED}Error: Key name required${NC}" echo "Usage: $0 create [--admin]" exit 1 fi local key_id=$(generate_key_id "$name") local secret=$(generate_key) local api_key="sk-${secret}" local created_at=$(date -Iseconds) local scope="standard" [ "$is_admin" = "--admin" ] && scope="admin" # Save to keys file (key_id|name|scope|created_at|hash) local key_hash=$(echo -n "$api_key" | sha256sum | cut -d' ' -f1) echo "${key_id}|${name}|${scope}|${created_at}|${key_hash}" >> "$KEYS_FILE" # Update nginx map rebuild_nginx_map log_action "CREATE" "$key_id" "name=$name scope=$scope" echo -e "${GREEN}API Key Created${NC}" echo "================================" echo "Key ID: $key_id" echo "Name: $name" echo "Scope: $scope" echo "API Key: $api_key" echo "================================" echo -e "${YELLOW}WARNING: Save this key now. It cannot be retrieved later.${NC}" } # Revoke API key revoke_key() { local key_id="$1" if [ -z "$key_id" ]; then echo -e "${RED}Error: Key ID required${NC}" echo "Usage: $0 revoke " exit 1 fi if ! grep -q "^${key_id}|" "$KEYS_FILE" 2>/dev/null; then echo -e "${RED}Error: Key ID not found${NC}" exit 1 fi # Remove from keys file sed -i "/^${key_id}|/d" "$KEYS_FILE" # Rebuild nginx map rebuild_nginx_map log_action "REVOKE" "$key_id" "revoked" echo -e "${GREEN}Key revoked: $key_id${NC}" echo "Run 'nginx -s reload' to apply changes" } # List all keys (without secrets) list_keys() { echo "API Keys" echo "========" echo "" printf "%-40s %-15s %-10s %s\n" "KEY_ID" "NAME" "SCOPE" "CREATED" printf "%-40s %-15s %-10s %s\n" "------" "----" "-----" "-------" if [ -f "$KEYS_FILE" ] && [ -s "$KEYS_FILE" ]; then while IFS='|' read -r key_id name scope created_at hash; do printf "%-40s %-15s %-10s %s\n" "$key_id" "$name" "$scope" "${created_at:0:19}" done < "$KEYS_FILE" else echo "(no keys)" fi echo "" echo "Total: $(wc -l < "$KEYS_FILE" 2>/dev/null || echo 0) keys" } # Verify a key (returns key_id if valid) verify_key() { local api_key="$1" if [ -z "$api_key" ]; then echo -e "${RED}Error: API key required${NC}" exit 1 fi local key_hash=$(echo -n "$api_key" | sha256sum | cut -d' ' -f1) while IFS='|' read -r key_id name scope created_at stored_hash; do if [ "$key_hash" = "$stored_hash" ]; then echo -e "${GREEN}Valid${NC}" echo "Key ID: $key_id" echo "Name: $name" echo "Scope: $scope" log_action "VERIFY" "$key_id" "valid" exit 0 fi done < "$KEYS_FILE" echo -e "${RED}Invalid key${NC}" log_action "VERIFY" "unknown" "invalid" exit 1 } # Rotate health token rotate_health_token() { local new_token=$(generate_key) local old_token="" # Read old token if exists if [ -f "$HEALTH_TOKEN_FILE" ] && [ -s "$HEALTH_TOKEN_FILE" ]; then old_token=$(grep "^current=" "$HEALTH_TOKEN_FILE" | cut -d'=' -f2) fi # Write new config (current + previous for grace period) cat > "$HEALTH_TOKEN_FILE" << EOF # Health Token Configuration # Generated: $(date -Iseconds) # Previous token valid for 24h after rotation current=$new_token previous=$old_token rotated_at=$(date -Iseconds) EOF chmod 600 "$HEALTH_TOKEN_FILE" # Update nginx include cat > "$KEYS_DIR/nginx-health-token.conf" << EOF # Auto-generated health token map # Do not edit manually map \$http_x_health_token \$health_token_valid { default 0; "$new_token" 1; EOF # Add previous token if exists (grace period) if [ -n "$old_token" ]; then echo " \"$old_token\" 1; # previous (grace period)" >> "$KEYS_DIR/nginx-health-token.conf" fi echo "}" >> "$KEYS_DIR/nginx-health-token.conf" chmod 600 "$KEYS_DIR/nginx-health-token.conf" log_action "ROTATE_HEALTH" "-" "token rotated" echo -e "${GREEN}Health Token Rotated${NC}" echo "================================" echo "New Token: $new_token" echo "================================" echo -e "${YELLOW}Update your monitoring systems with the new token.${NC}" echo "Previous token remains valid for grace period." echo "Run 'nginx -s reload' to apply changes" } # Rebuild nginx API keys map rebuild_nginx_map() { cat > "$NGINX_KEYS_MAP" << 'HEADER' # Auto-generated API keys map # Do not edit manually - use api-keys.sh # Format: key hash -> "key_id:scope" map $api_key_hash $api_key_info { default ""; HEADER while IFS='|' read -r key_id name scope created_at hash; do echo " \"$hash\" \"$key_id:$scope\";" >> "$NGINX_KEYS_MAP" done < "$KEYS_FILE" echo "}" >> "$NGINX_KEYS_MAP" chmod 600 "$NGINX_KEYS_MAP" } # Show usage usage() { echo "NODE1 API Key Management" echo "" echo "Usage:" echo " $0 create [--admin] Create new API key" echo " $0 revoke Revoke API key" echo " $0 list List all keys" echo " $0 verify Verify API key" echo " $0 rotate-health Rotate health check token" echo "" echo "Examples:" echo " $0 create monitoring" echo " $0 create admin-user --admin" echo " $0 revoke kid_monitoring_20260126_abc123" } # Main init_dirs case "${1:-}" in create) create_key "$2" "$3" ;; revoke) revoke_key "$2" ;; list) list_keys ;; verify) verify_key "$2" ;; rotate-health) rotate_health_token ;; *) usage ;; esac