230 lines
5.4 KiB
Bash
Executable File
230 lines
5.4 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Cloudflare API credentials
|
|
CF_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}"
|
|
CF_ZONE_ID="${CLOUDFLARE_ZONE_ID:-}"
|
|
|
|
# Dictionary files
|
|
DICT_FILE="/usr/share/dict/words"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
usage() {
|
|
echo "Usage: $0 --hostname <hostname> --ip <ip_address>"
|
|
echo " $0 --random --domain <domain> --ip <ip_address>"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --hostname Specific hostname to add (e.g., test.example.com)"
|
|
echo " --random Generate random hostname"
|
|
echo " --domain Base domain for random hostname (e.g., example.org)"
|
|
echo " --ip IP address for A record"
|
|
echo ""
|
|
echo "Environment variables required:"
|
|
echo " CLOUDFLARE_API_TOKEN"
|
|
echo " CLOUDFLARE_ZONE_ID"
|
|
exit 1
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1" >&2
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
|
|
}
|
|
|
|
log_info() {
|
|
echo -e "${YELLOW}[INFO]${NC} $1" >&2
|
|
}
|
|
|
|
check_requirements() {
|
|
if [[ -z "$CF_API_TOKEN" ]]; then
|
|
log_error "CLOUDFLARE_API_TOKEN environment variable not set"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$CF_ZONE_ID" ]]; then
|
|
log_error "CLOUDFLARE_ZONE_ID environment variable not set"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v curl &> /dev/null; then
|
|
log_error "curl is required but not installed"
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v jq &> /dev/null; then
|
|
log_error "jq is required but not installed"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
get_random_word() {
|
|
if [[ ! -f "$DICT_FILE" ]]; then
|
|
log_error "Dictionary file not found: $DICT_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Get random word: lowercase, letters only, 3-10 characters
|
|
grep -E '^[a-z]{3,10}$' "$DICT_FILE" | shuf -n 1
|
|
}
|
|
|
|
generate_random_hostname() {
|
|
local domain=$1
|
|
local word1=$(get_random_word)
|
|
local word2=$(get_random_word)
|
|
echo "${word1}-${word2}.${domain}"
|
|
}
|
|
|
|
check_dns_exists() {
|
|
local hostname=$1
|
|
|
|
log_info "Checking if DNS record exists for: $hostname"
|
|
|
|
local response=$(curl -s -X GET \
|
|
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records?name=${hostname}" \
|
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
|
-H "Content-Type: application/json")
|
|
|
|
local success=$(echo "$response" | jq -r '.success')
|
|
|
|
if [[ "$success" != "true" ]]; then
|
|
log_error "Cloudflare API request failed"
|
|
echo "$response" | jq '.'
|
|
exit 1
|
|
fi
|
|
|
|
local count=$(echo "$response" | jq -r '.result | length')
|
|
|
|
if [[ "$count" -gt 0 ]]; then
|
|
return 0 # Record exists
|
|
else
|
|
return 1 # Record does not exist
|
|
fi
|
|
}
|
|
|
|
add_dns_record() {
|
|
local hostname=$1
|
|
local ip=$2
|
|
|
|
log_info "Adding DNS record: $hostname -> $ip"
|
|
|
|
local response=$(curl -s -X POST \
|
|
"https://api.cloudflare.com/client/v4/zones/${CF_ZONE_ID}/dns_records" \
|
|
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
--data "{
|
|
\"type\": \"A\",
|
|
\"name\": \"${hostname}\",
|
|
\"content\": \"${ip}\",
|
|
\"ttl\": 1,
|
|
\"proxied\": false
|
|
}")
|
|
|
|
local success=$(echo "$response" | jq -r '.success')
|
|
|
|
if [[ "$success" == "true" ]]; then
|
|
log_success "DNS record added successfully: $hostname -> $ip"
|
|
echo "$response" | jq -r '.result | "Record ID: \(.id)"'
|
|
return 0
|
|
else
|
|
log_error "Failed to add DNS record"
|
|
echo "$response" | jq '.'
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Parse arguments
|
|
HOSTNAME=""
|
|
IP=""
|
|
RANDOM_MODE=false
|
|
DOMAIN=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--hostname)
|
|
HOSTNAME="$2"
|
|
shift 2
|
|
;;
|
|
--ip)
|
|
IP="$2"
|
|
shift 2
|
|
;;
|
|
--random)
|
|
RANDOM_MODE=true
|
|
shift
|
|
;;
|
|
--domain)
|
|
DOMAIN="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
;;
|
|
*)
|
|
log_error "Unknown option: $1"
|
|
usage
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Validate arguments
|
|
if [[ -z "$IP" ]]; then
|
|
log_error "IP address is required"
|
|
usage
|
|
fi
|
|
|
|
if [[ "$RANDOM_MODE" == true ]]; then
|
|
if [[ -z "$DOMAIN" ]]; then
|
|
log_error "Domain is required when using --random mode"
|
|
usage
|
|
fi
|
|
else
|
|
if [[ -z "$HOSTNAME" ]]; then
|
|
log_error "Hostname is required"
|
|
usage
|
|
fi
|
|
fi
|
|
|
|
# Check requirements
|
|
check_requirements
|
|
|
|
# Generate or use provided hostname
|
|
if [[ "$RANDOM_MODE" == true ]]; then
|
|
MAX_ATTEMPTS=50
|
|
attempt=1
|
|
|
|
while [[ $attempt -le $MAX_ATTEMPTS ]]; do
|
|
HOSTNAME=$(generate_random_hostname "$DOMAIN")
|
|
log_info "Generated hostname (attempt $attempt): $HOSTNAME"
|
|
|
|
if ! check_dns_exists "$HOSTNAME"; then
|
|
log_success "Hostname is available: $HOSTNAME"
|
|
break
|
|
else
|
|
log_info "Hostname already exists, generating new one..."
|
|
attempt=$((attempt + 1))
|
|
fi
|
|
done
|
|
|
|
if [[ $attempt -gt $MAX_ATTEMPTS ]]; then
|
|
log_error "Failed to generate unique hostname after $MAX_ATTEMPTS attempts"
|
|
exit 1
|
|
fi
|
|
else
|
|
if check_dns_exists "$HOSTNAME"; then
|
|
log_error "DNS record already exists for: $HOSTNAME"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Add the DNS record
|
|
add_dns_record "$HOSTNAME" "$IP"
|
|
|