diff options
author | doc <doc@filenotfound.org> | 2025-06-30 20:14:17 +0000 |
---|---|---|
committer | doc <doc@filenotfound.org> | 2025-06-30 20:14:17 +0000 |
commit | a8cd1c324c0541b0d26542168aeced085ec13201 (patch) | |
tree | a99d398008b46aa4df5dcae997e1690298d2fc70 /functions |
Diffstat (limited to 'functions')
-rwxr-xr-x | functions/destroy_vps_by_label.sh | 28 | ||||
-rwxr-xr-x | functions/disable_backups_by_label.sh | 23 | ||||
-rw-r--r-- | functions/disable_ip.sh | 18 | ||||
-rwxr-xr-x | functions/enable_backups_by_label.sh | 23 | ||||
-rwxr-xr-x | functions/list_all_vps.sh | 9 | ||||
-rwxr-xr-x | functions/provision.sh | 135 | ||||
-rwxr-xr-x | functions/reboot_vps.sh | 7 | ||||
-rwxr-xr-x | functions/resize_vps.sh | 27 | ||||
-rwxr-xr-x | functions/safe_create_dataset.sh | 12 | ||||
-rwxr-xr-x | functions/status_vps.sh | 8 | ||||
-rwxr-xr-x | functions/usage.sh | 22 | ||||
-rwxr-xr-x | functions/verify_ptr.sh | 29 |
12 files changed, 341 insertions, 0 deletions
diff --git a/functions/destroy_vps_by_label.sh b/functions/destroy_vps_by_label.sh new file mode 100755 index 0000000..09d807e --- /dev/null +++ b/functions/destroy_vps_by_label.sh @@ -0,0 +1,28 @@ +destroy_vps_by_label() { + LABEL="$1" + echo "Looking for VPS with label '$LABEL'..." + LINODE_ID=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .id') + + if [ -z "$LINODE_ID" ]; then + echo "Error: No Linode found with label '$LABEL'" + exit 1 + fi + + read -rp "Are you sure you want to destroy VPS '$LABEL' (ID: $LINODE_ID)? [y/N] " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + echo "Destroying Linode with ID $LINODE_ID (label: $LABEL)..." + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X DELETE \ + https://api.linode.com/v4/linode/instances/$LINODE_ID \ + -H "Authorization: Bearer $LINODE_API_TOKEN") + + if [[ "$HTTP_STATUS" == "204" ]]; then + echo "✅ Linode $LABEL (ID $LINODE_ID) has been destroyed." + else + echo "❌ Failed to destroy VPS. HTTP status: $HTTP_STATUS" + fi + else + echo "Cancelled. VPS '$LABEL' not destroyed." + fi +} diff --git a/functions/disable_backups_by_label.sh b/functions/disable_backups_by_label.sh new file mode 100755 index 0000000..417bdb8 --- /dev/null +++ b/functions/disable_backups_by_label.sh @@ -0,0 +1,23 @@ +disable_backups_by_label() { + LABEL="$1" + LINODE_ID=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .id') + + if [ -z "$LINODE_ID" ]; then + echo "❌ No Linode found with label '$LABEL'" + exit 1 + fi + + echo "Disabling backups for Linode '$LABEL' (ID: $LINODE_ID)..." + + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ + https://api.linode.com/v4/linode/instances/$LINODE_ID/backups/disable \ + -H "Authorization: Bearer $LINODE_API_TOKEN") + + if [[ "$HTTP_STATUS" == "200" ]]; then + echo "✅ Backups disabled for Linode $LABEL." + else + echo "❌ Failed to disable backups (HTTP $HTTP_STATUS)" + fi +} diff --git a/functions/disable_ip.sh b/functions/disable_ip.sh new file mode 100644 index 0000000..0021b74 --- /dev/null +++ b/functions/disable_ip.sh @@ -0,0 +1,18 @@ +disable_ip() { + local ip="$1" + + if [[ -z "$ip" ]]; then + echo "[!] No IP specified." + exit 1 + fi + + echo "[*] Disabling access to VPS with IP: $ip" + + # Block all traffic to/from that IP via iptables + iptables -A INPUT -s "$ip" -j DROP + iptables -A OUTPUT -d "$ip" -j DROP + + echo "$ip - disabled on $(date)" >> /var/log/genesis-disabled.log + + echo "[✓] $ip has been blocked and logged." +} diff --git a/functions/enable_backups_by_label.sh b/functions/enable_backups_by_label.sh new file mode 100755 index 0000000..08fb31d --- /dev/null +++ b/functions/enable_backups_by_label.sh @@ -0,0 +1,23 @@ +enable_backups_by_label() { + LABEL="$1" + LINODE_ID=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .id') + + if [ -z "$LINODE_ID" ]; then + echo "❌ No Linode found with label '$LABEL'" + exit 1 + fi + + echo "Enabling backups for Linode '$LABEL' (ID: $LINODE_ID)..." + + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ + https://api.linode.com/v4/linode/instances/$LINODE_ID/backups/enable \ + -H "Authorization: Bearer $LINODE_API_TOKEN") + + if [[ "$HTTP_STATUS" == "200" ]]; then + echo "✅ Backups enabled for Linode $LABEL." + else + echo "❌ Failed to enable backups (HTTP $HTTP_STATUS)" + fi +} diff --git a/functions/list_all_vps.sh b/functions/list_all_vps.sh new file mode 100755 index 0000000..8ce99eb --- /dev/null +++ b/functions/list_all_vps.sh @@ -0,0 +1,9 @@ +list_all_vps() { + curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r ' + .data[] | [.label, .id, .region, .type, .ipv4[0], .status] | + @tsv' | column -t -s $'\t' | \ + awk 'BEGIN { print "LABEL ID REGION TYPE IP STATUS" } + { printf "%-11s %-10s %-10s %-16s %-15s %s\n", $1, $2, $3, $4, $5, $6 }' +} diff --git a/functions/provision.sh b/functions/provision.sh new file mode 100755 index 0000000..f6e9d39 --- /dev/null +++ b/functions/provision.sh @@ -0,0 +1,135 @@ +provision_vps() { + LABEL="$1" + REGION="$2" + TYPE="$3" + IMAGE="$4" + ROOT_PASS="${5:-$(openssl rand -base64 16)}" + + if [[ "$LINODE_API_TOKEN" == "REPLACE_WITH_YOUR_LINODE_API_TOKEN" ]]; then + echo "❌ Error: You must set your LINODE_API_TOKEN at the top of this script." + exit 1 + fi + + CLOUD_INIT=$(cat <<EOF +#cloud-config +hostname: genesis-vps +manage_etc_hosts: true +write_files: + - path: /usr/local/bin/genesis_squeaky.sh + permissions: '0755' + content: | + #!/bin/bash + set -e + GEN_HOSTNAME="genesis-vps-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 6)" + LOGDIR="/home/doc/vpslogs" + LOGFILE="$LOGDIR/$GEN_HOSTNAME.log" + IP_ADDR=$(hostname -I | awk '{print $1}') + + iptables -A OUTPUT -p icmp --icmp-type time-exceeded -j DROP + iptables -A INPUT -p udp --dport 33434:33534 -j DROP + iptables -A INPUT -p tcp --dport 33434:33534 -j DROP + + hostnamectl set-hostname "$GEN_HOSTNAME" + sed -i "s/^127.0.1.1.*/127.0.1.1 $GEN_HOSTNAME/" /etc/hosts + + systemctl stop linode-cloudinit 2>/dev/null || true + systemctl disable linode-cloudinit 2>/dev/null || true + touch /etc/cloud/cloud-init.disabled + rm -rf /etc/cloud /var/lib/cloud /var/log/cloud-init.log + + rm -f /etc/motd /etc/update-motd.d/linode + rm -rf /usr/share/linode* + rm -f /etc/apt/sources.list.d/linode.list + apt remove --purge -y linode-cli linode-config 2>/dev/null || true + + echo "[genesisctl] Attempting to log to Krang via webhook..." >> /var/log/genesis-harden.log + curl -s -X POST -H "Content-Type: application/json" \ + -d "{\"host\": \"$GEN_HOSTNAME\", \"ip\": \"$IP_ADDR\", \"timestamp\": \"$(date)\"}" \ + http://krang.core.sshjunkie.com:8080/genesislog >> /var/log/genesis-harden.log 2>&1 || echo "[genesisctl] Krang webhook logging failed" >> /var/log/genesis-harden.log + + touch /var/log/genesis-hardened.ok + +runcmd: + - [ bash, /usr/local/bin/genesis_squeaky.sh ] +EOF +) + + USER_DATA=$(echo "$CLOUD_INIT" | base64 -w 0) + + echo "Provisioning VPS '$LABEL' in $REGION with type $TYPE and image $IMAGE..." + TMP_FILE=$(mktemp) + JSON_PAYLOAD=$(cat <<EOF +{ + "label": "$LABEL", + "region": "$REGION", + "type": "$TYPE", + "image": "$IMAGE", + "authorized_users": [], + "root_pass": "$ROOT_PASS", + "booted": true, + "metadata": { + "user_data": "$USER_DATA" + } +} +EOF +) + + HTTP_STATUS=$(curl -s -o "$TMP_FILE" -w "%{http_code}" -X POST https://api.linode.com/v4/linode/instances \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $LINODE_API_TOKEN" \ + -d "$JSON_PAYLOAD") + + echo -e "\n--- HTTP STATUS: $HTTP_STATUS ---" + echo "--- RAW RESPONSE: ---" + cat "$TMP_FILE" + + if [[ "$HTTP_STATUS" != "200" && "$HTTP_STATUS" != "201" ]]; then + echo -e "\n❌ Failed to provision VPS (HTTP $HTTP_STATUS)" + jq . "$TMP_FILE" + exit 1 + fi + + echo -e "\n✅ VPS provisioned:" + IP=$(jq -r '.ipv4[0]' "$TMP_FILE") + LINODE_ID=$(jq -r '.id' "$TMP_FILE") + echo "Label: $LABEL" + echo "IP Address: $IP" + echo "Root Password: $ROOT_PASS" + + # Add DNS record to Cloudflare + echo "📡 Adding A record for $LABEL.$CF_DOMAIN → $IP..." + echo "[DEBUG] CF_API_TOKEN=$CF_API_TOKEN" + 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-binary @<(cat <<JSON +{ + "type": "A", + "name": "$LABEL.$CF_DOMAIN", + "content": "$IP", + "ttl": 120, + "proxied": false +} +JSON +) | jq '.success, .errors, .messages' + + echo "⏳ Waiting indefinitely for DNS to propagate before setting rDNS..." +i=1 +while true; do + CURRENT_IP=$(dig +short "$LABEL.$CF_DOMAIN") + if [[ "$CURRENT_IP" == "$IP" ]]; then + echo "✅ A record resolved. Setting rDNS..." + curl -s -X PUT "https://api.linode.com/v4/linode/instances/$LINODE_ID/ips/$IP" \ + -H "Authorization: Bearer $LINODE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"rdns": "'"$LABEL.$CF_DOMAIN"'"}' + break + fi + echo "⏳ Attempt $i: DNS not ready. Waiting 15s..." + sleep 15 + ((i++)) +done + + + echo "$LINODE_ID|$IP|$LABEL" >> /home/doc/vpslogs/pending_rdns.log +} diff --git a/functions/reboot_vps.sh b/functions/reboot_vps.sh new file mode 100755 index 0000000..2741b9c --- /dev/null +++ b/functions/reboot_vps.sh @@ -0,0 +1,7 @@ +reboot_vps() { + LINODE_ID="$1" + echo "Rebooting Linode VPS ID $LINODE_ID..." + + curl -s -X POST https://api.linode.com/v4/linode/instances/$LINODE_ID/reboot \ + -H "Authorization: Bearer $LINODE_API_TOKEN" | jq +} diff --git a/functions/resize_vps.sh b/functions/resize_vps.sh new file mode 100755 index 0000000..c06ea91 --- /dev/null +++ b/functions/resize_vps.sh @@ -0,0 +1,27 @@ +resize_vps() { + LABEL="$1" + NEW_TYPE="$2" + + LINODE_ID=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .id') + + if [ -z "$LINODE_ID" ]; then + echo "❌ No Linode found with label '$LABEL'" + exit 1 + fi + + echo "Resizing Linode '$LABEL' to type '$NEW_TYPE'..." + + HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X POST \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $LINODE_API_TOKEN" \ + -d '{"type": "'"$NEW_TYPE"'"}' \ + https://api.linode.com/v4/linode/instances/$LINODE_ID/resize) + + if [[ "$HTTP_STATUS" == "200" ]]; then + echo "✅ Linode $LABEL resized to $NEW_TYPE." + else + echo "❌ Failed to resize VPS. HTTP status: $HTTP_STATUS" + fi +} diff --git a/functions/safe_create_dataset.sh b/functions/safe_create_dataset.sh new file mode 100755 index 0000000..1960e55 --- /dev/null +++ b/functions/safe_create_dataset.sh @@ -0,0 +1,12 @@ +safe_create_dataset() { + FULLPATH="$1" + + # Remove any trailing slash + FULLPATH="${FULLPATH%/}" + + POOL="${FULLPATH%%/*}" + DATASET="${FULLPATH#*/}" + + echo "🛰 Connecting to Shredder to safely create '${POOL}/${DATASET}'..." + ssh shredder "/usr/local/bin/genesis-safe-zfs.sh $POOL $DATASET" +} diff --git a/functions/status_vps.sh b/functions/status_vps.sh new file mode 100755 index 0000000..91996e9 --- /dev/null +++ b/functions/status_vps.sh @@ -0,0 +1,8 @@ +status_vps() { + LABEL="$1" + curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" \ + https://api.linode.com/v4/linode/instances | \ + jq -r --arg LABEL "$LABEL" ' + .data[] | select(.label == $LABEL) | + "Label: \(.label)\nID: \(.id)\nRegion: \(.region)\nType: \(.type)\nStatus: \(.status)\nIP: \(.ipv4[0])\nCreated: \(.created)"' +} diff --git a/functions/usage.sh b/functions/usage.sh new file mode 100755 index 0000000..25861b8 --- /dev/null +++ b/functions/usage.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +function usage() { + echo "Usage: genesisctl [command]" + echo "Commands:" + echo " watch-abuse Start abuse monitoring via IPTables" +} + +function watch_abuse() { + echo "[*] Launching abuse watch via screen..." + screen -dmS abusewatch /usr/local/bin/genesisctl-watch-abuse.sh + echo "[✓] Abuse watch running in detached screen session 'abusewatch'" +} + +case "$1" in + watch-abuse) + watch_abuse + ;; + *) + usage + ;; +esac diff --git a/functions/verify_ptr.sh b/functions/verify_ptr.sh new file mode 100755 index 0000000..8ce2f6c --- /dev/null +++ b/functions/verify_ptr.sh @@ -0,0 +1,29 @@ +verify_ptr() { + LABEL="$1" + IP=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" https://api.linode.com/v4/linode/instances \ + | jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .ipv4[0]') + LINODE_ID=$(curl -s -H "Authorization: Bearer $LINODE_API_TOKEN" https://api.linode.com/v4/linode/instances \ + | jq -r --arg LABEL "$LABEL" '.data[] | select(.label == $LABEL) | .id') + + if [[ -z "$IP" || -z "$LINODE_ID" ]]; then + echo "❌ Could not retrieve IP or Linode ID for label '$LABEL'" + return 1 + fi + + echo "Re-attempting rDNS update for $LABEL ($IP)..." + PTR_NAME="${LABEL}.doinkle.pro" + RDNS_PAYLOAD=$(cat <<EOF +{ + "rdns": "$PTR_NAME" +} +EOF +) + + RESPONSE=$(curl -s -w "\nHTTP Status: %{http_code}\n" -X PUT \ + -H "Authorization: Bearer $LINODE_API_TOKEN" \ + -H "Content-Type: application/json" \ + -d "$RDNS_PAYLOAD" \ + "https://api.linode.com/v4/linode/instances/$LINODE_ID/ips/$IP") + + echo "$RESPONSE" +} |