Auto-commit from giteapush.sh at 2025-05-21 16:08:22
This commit is contained in:
parent
a1eb312c1f
commit
eb5d1a572f
61
documents/fztodo.md
Normal file
61
documents/fztodo.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
FailZero TODO List
|
||||||
|
✅ Completed
|
||||||
|
|
||||||
|
fz_ip_validator.py runs on Krang with systemd and venv
|
||||||
|
|
||||||
|
Logging to /var/log/failzero/ip_validator.log
|
||||||
|
|
||||||
|
IP abuse detection via /validate endpoint
|
||||||
|
|
||||||
|
PayPal billing form with terminal-style UI
|
||||||
|
|
||||||
|
Telegram alerts on order
|
||||||
|
|
||||||
|
Abuse watcher with threshold-based disable
|
||||||
|
|
||||||
|
genesisctl disable --ip blocks outbound traffic
|
||||||
|
|
||||||
|
Screen-based background runner (genesisctl watch-abuse)
|
||||||
|
|
||||||
|
🧠 Next Steps (Active TODO List)
|
||||||
|
🔒 Abuse Management
|
||||||
|
|
||||||
|
Build /api/report endpoint to manually flag IPs from Krang or external tools
|
||||||
|
|
||||||
|
Switch abuse_list in fz_ip_validator.py to file-based or Redis-backed source
|
||||||
|
|
||||||
|
Log confirmed abuse incidents to /var/log/genesis-abuse-confirmed.log
|
||||||
|
|
||||||
|
🌐 Frontend Integration
|
||||||
|
|
||||||
|
Modify billing HTML to call /validate before starting PayPal process
|
||||||
|
|
||||||
|
Display an error if IP is flagged (valid === false) and block purchase
|
||||||
|
|
||||||
|
Show dynamic pricing and risk flags in the form using the validator output
|
||||||
|
|
||||||
|
💳 Billing + Provision
|
||||||
|
|
||||||
|
Hook PayPal IPN or success return URL to trigger VPS creation
|
||||||
|
|
||||||
|
Match PayPal TXID to IP + label and log it
|
||||||
|
|
||||||
|
Generate reverse DNS automatically on provision (e.g., nighthawk01.failzero.net)
|
||||||
|
|
||||||
|
Add /privacy and /terms static pages to keep things legally clean
|
||||||
|
|
||||||
|
⚙️ Tooling & UX
|
||||||
|
|
||||||
|
Add genesisctl enable --ip to unblock previously flagged IPs
|
||||||
|
|
||||||
|
Add genesisctl status --ip to query abuse hits / log activity
|
||||||
|
|
||||||
|
Optionally hash or sign each VPS order for non-repudiation audit trail
|
||||||
|
|
||||||
|
🧪 Optional / Nice-to-Have
|
||||||
|
|
||||||
|
Build a minimal dashboard or log viewer for flagged IPs
|
||||||
|
|
||||||
|
Rate-limit /validate via nginx or Flask limiter
|
||||||
|
|
||||||
|
Replace all external IP tools with internal validator
|
35
miscellaneous/python/fz_ip_validator.py
Normal file
35
miscellaneous/python/fz_ip_validator.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# fz_ip_validator.py
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
# Configure logging
|
||||||
|
logging.basicConfig(filename='/home/doc/ip_validator.log', level=logging.INFO)
|
||||||
|
|
||||||
|
# Sample in-memory abuse list (replace with file/db lookup in production)
|
||||||
|
abuse_list = set([
|
||||||
|
'1.2.3.4', # example flagged IP
|
||||||
|
'5.6.7.8'
|
||||||
|
])
|
||||||
|
|
||||||
|
@app.route('/validate', methods=['GET'])
|
||||||
|
def validate():
|
||||||
|
ip = request.args.get('ip', request.remote_addr)
|
||||||
|
now = datetime.utcnow().isoformat()
|
||||||
|
|
||||||
|
# Log the IP
|
||||||
|
logging.info(f"{now} - IP validation request from {ip}")
|
||||||
|
|
||||||
|
# Check if IP is in abuse list
|
||||||
|
is_abusive = ip in abuse_list
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"ip": ip,
|
||||||
|
"valid": not is_abusive,
|
||||||
|
"reason": "Flagged for abuse" if is_abusive else "OK"
|
||||||
|
})
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=5024)
|
26
miscellaneous/python/webhook.py
Executable file
26
miscellaneous/python/webhook.py
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
from flask import Flask, request, jsonify
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
LOG_DIR = "/home/doc/vpslogs"
|
||||||
|
os.makedirs(LOG_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
@app.route("/genesislog", methods=["POST"])
|
||||||
|
def genesis_log():
|
||||||
|
data = request.get_json()
|
||||||
|
if not data or "host" not in data or "ip" not in data:
|
||||||
|
return jsonify({"error": "Invalid data"}), 400
|
||||||
|
|
||||||
|
host = data["host"]
|
||||||
|
ip = data["ip"]
|
||||||
|
timestamp = data.get("timestamp", datetime.utcnow().isoformat())
|
||||||
|
logfile = os.path.join(LOG_DIR, f"{host}.log")
|
||||||
|
|
||||||
|
with open(logfile, "a") as f:
|
||||||
|
f.write(f"{timestamp} - {host} ({ip}) deployed and hardened.\n")
|
||||||
|
|
||||||
|
return jsonify({"status": "logged"}), 200
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=8080)
|
5
miscellaneous/vps/.env
Normal file
5
miscellaneous/vps/.env
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
LINODE_API_TOKEN=8140523e8d64f16f490b70096b04d221a44236eda552b0caa35fe9be35442f6d
|
||||||
|
# Cloudflare API
|
||||||
|
CF_API_TOKEN="PrUbZD1bj0ky1T32waiis2hp91e4Az1ZiCule9Ys"
|
||||||
|
CF_ZONE_ID="c9b0c727c2c55594f62d38227133e3ac"
|
||||||
|
CF_DOMAIN="failzero.net"
|
37
miscellaneous/vps/check-hardened.sh
Executable file
37
miscellaneous/vps/check-hardened.sh
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# check-hardened.sh - Scan all known Genesis VPSes for hardening status
|
||||||
|
# Requirements: ssh access to all VPSes by label or IP
|
||||||
|
|
||||||
|
LOG_BASE="/home/doc/vpslogs"
|
||||||
|
MARKER_FILE="/var/log/genesis-hardened.ok"
|
||||||
|
|
||||||
|
if [ ! -d "$LOG_BASE" ]; then
|
||||||
|
echo "❌ Log directory $LOG_BASE does not exist. Are you running this on Krang?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$LOG_BASE" || exit 1
|
||||||
|
|
||||||
|
echo "🔍 Scanning for hardened Genesis VPSes..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
for LOG in *.log; do
|
||||||
|
VPS_LABEL="${LOG%.log}"
|
||||||
|
LAST_KNOWN_IP=$(grep -Eo '\([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\)' "$LOG" | tail -1 | tr -d '()')
|
||||||
|
|
||||||
|
if [ -z "$LAST_KNOWN_IP" ]; then
|
||||||
|
echo "⚠️ $VPS_LABEL: No IP found in log. Skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -n "🔧 $VPS_LABEL ($LAST_KNOWN_IP): "
|
||||||
|
|
||||||
|
ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@"$LAST_KNOWN_IP" "test -f $MARKER_FILE" >/dev/null 2>&1
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ Hardened"
|
||||||
|
else
|
||||||
|
echo "❌ Not marked as hardened"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
27
miscellaneous/vps/check_rdns_retry.sh
Executable file
27
miscellaneous/vps/check_rdns_retry.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
[ -f ".env" ] && source .env
|
||||||
|
LOGFILE="/home/doc/vpslogs/pending_rdns.log"
|
||||||
|
TMPFILE="/tmp/rdns_retry.log"
|
||||||
|
|
||||||
|
touch "$TMPFILE"
|
||||||
|
|
||||||
|
while IFS="|" read -r LINODE_ID IP LABEL; do
|
||||||
|
CURRENT_RDNS=$(dig -x "$IP" +short)
|
||||||
|
EXPECTED_RDNS="$LABEL.failzero.net."
|
||||||
|
|
||||||
|
if [[ "$CURRENT_RDNS" == "$EXPECTED_RDNS" ]]; then
|
||||||
|
echo "✅ $IP already has correct rDNS ($CURRENT_RDNS)"
|
||||||
|
else
|
||||||
|
echo "⏳ rDNS not set correctly for $LABEL ($IP). Retrying..."
|
||||||
|
RESPONSE=$(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.failzero.net"'"}')
|
||||||
|
echo "🔁 Retry result for $IP: $RESPONSE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$LINODE_ID|$IP|$LABEL" >> "$TMPFILE"
|
||||||
|
done < "$LOGFILE"
|
||||||
|
|
||||||
|
mv "$TMPFILE" "$LOGFILE"
|
28
miscellaneous/vps/functions/destroy_vps_by_label.sh
Executable file
28
miscellaneous/vps/functions/destroy_vps_by_label.sh
Executable file
@ -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
|
||||||
|
}
|
23
miscellaneous/vps/functions/disable_backups_by_label.sh
Executable file
23
miscellaneous/vps/functions/disable_backups_by_label.sh
Executable file
@ -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
|
||||||
|
}
|
18
miscellaneous/vps/functions/disable_ip.sh
Normal file
18
miscellaneous/vps/functions/disable_ip.sh
Normal file
@ -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."
|
||||||
|
}
|
23
miscellaneous/vps/functions/enable_backups_by_label.sh
Executable file
23
miscellaneous/vps/functions/enable_backups_by_label.sh
Executable file
@ -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
|
||||||
|
}
|
9
miscellaneous/vps/functions/list_all_vps.sh
Executable file
9
miscellaneous/vps/functions/list_all_vps.sh
Executable file
@ -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 }'
|
||||||
|
}
|
135
miscellaneous/vps/functions/provision.sh
Executable file
135
miscellaneous/vps/functions/provision.sh
Executable file
@ -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
|
||||||
|
}
|
7
miscellaneous/vps/functions/reboot_vps.sh
Executable file
7
miscellaneous/vps/functions/reboot_vps.sh
Executable file
@ -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
|
||||||
|
}
|
27
miscellaneous/vps/functions/resize_vps.sh
Executable file
27
miscellaneous/vps/functions/resize_vps.sh
Executable file
@ -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
|
||||||
|
}
|
12
miscellaneous/vps/functions/safe_create_dataset.sh
Executable file
12
miscellaneous/vps/functions/safe_create_dataset.sh
Executable file
@ -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"
|
||||||
|
}
|
8
miscellaneous/vps/functions/status_vps.sh
Executable file
8
miscellaneous/vps/functions/status_vps.sh
Executable file
@ -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)"'
|
||||||
|
}
|
22
miscellaneous/vps/functions/usage.sh
Executable file
22
miscellaneous/vps/functions/usage.sh
Executable file
@ -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
|
29
miscellaneous/vps/functions/verify_ptr.sh
Executable file
29
miscellaneous/vps/functions/verify_ptr.sh
Executable file
@ -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"
|
||||||
|
}
|
44
miscellaneous/vps/genesis_squeaky.sh
Executable file
44
miscellaneous/vps/genesis_squeaky.sh
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# === CONFIG ===
|
||||||
|
GEN_HOSTNAME="genesis-vps-$RANDOM"
|
||||||
|
TG_API_URL="https://api.telegram.org/bot<OPTIONAL-BOT>/sendMessage"
|
||||||
|
TG_CHAT_ID="<OPTIONAL-ID>"
|
||||||
|
|
||||||
|
# === STEP 1: Obfuscate Traceroute (ICMP & UDP/TCP Ports) ===
|
||||||
|
echo "[*] Obfuscating traceroute and TTL paths..."
|
||||||
|
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
|
||||||
|
echo "[+] Firewall rules added."
|
||||||
|
|
||||||
|
# === STEP 2: Set a Neutral Hostname ===
|
||||||
|
echo "[*] Setting hostname to $GEN_HOSTNAME"
|
||||||
|
hostnamectl set-hostname "$GEN_HOSTNAME"
|
||||||
|
sed -i "s/^127.0.1.1.*/127.0.1.1 $GEN_HOSTNAME/" /etc/hosts
|
||||||
|
echo "[+] Hostname set."
|
||||||
|
|
||||||
|
# === STEP 3: Remove Linode Metadata Access ===
|
||||||
|
echo "[*] Disabling Linode metadata agent (if present)..."
|
||||||
|
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
|
||||||
|
echo "[+] Cloud-init neutered."
|
||||||
|
|
||||||
|
# === STEP 4: Scrub Linode Stuff ===
|
||||||
|
echo "[*] Scrubbing Linode fingerprints..."
|
||||||
|
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
|
||||||
|
yum remove -y linode-cli linode-config 2>/dev/null || true
|
||||||
|
echo "[+] Linode packages and branding removed."
|
||||||
|
|
||||||
|
# === STEP 5: Optional Telegram Notice ===
|
||||||
|
# Uncomment if you want to alert yourself when a VPS is hardened
|
||||||
|
# curl -s -X POST "$TG_API_URL" -d chat_id="$TG_CHAT_ID" -d text="Genesis VPS hardened: $GEN_HOSTNAME is stealth-ready." > /dev/null
|
||||||
|
|
||||||
|
# === STEP 6: Final Touch ===
|
||||||
|
echo "[✅] Genesis VPS hardened. You are now off-the-grid and good to go."
|
@ -29,106 +29,25 @@ PACKAGE_MASTODON_REGION="us-east"
|
|||||||
PACKAGE_MASTODON_TYPE="g6-standard-4"
|
PACKAGE_MASTODON_TYPE="g6-standard-4"
|
||||||
PACKAGE_MASTODON_IMAGE="linode/ubuntu22.04"
|
PACKAGE_MASTODON_IMAGE="linode/ubuntu22.04"
|
||||||
|
|
||||||
provision_vps() {
|
for f in functions/*.sh; do source "$f"; done
|
||||||
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
|
# Helper for DNS pre-propagation check (used after provisioning)
|
||||||
echo "❌ Error: You must set your LINODE_API_TOKEN at the top of this script."
|
await_dns_propagation() {
|
||||||
exit 1
|
HOSTNAME="$1"
|
||||||
fi
|
EXPECTED_IP="$2"
|
||||||
|
|
||||||
USER_DATA=$(echo "#cloud-config
|
echo "⏳ Waiting for DNS A record to propagate for $HOSTNAME to $EXPECTED_IP..."
|
||||||
packages:
|
for i in {1..10}; do
|
||||||
- rsync
|
ACTUAL_IP=$(dig +short "$HOSTNAME")
|
||||||
- fail2ban
|
if [[ "$ACTUAL_IP" == "$EXPECTED_IP" ]]; then
|
||||||
runcmd:
|
echo "✅ DNS A record found: $HOSTNAME → $EXPECTED_IP"
|
||||||
- curl -s https://help.sshjunkie.com/scripts/genesis-bootstrap.sh | bash" | base64 -w 0)
|
return 0
|
||||||
|
|
||||||
echo "Provisioning VPS '$LABEL' in $REGION with type $TYPE and image $IMAGE..."
|
|
||||||
echo "[DEBUG] Using API token prefix: ${LINODE_API_TOKEN:0:8}********"
|
|
||||||
echo "[DEBUG] JSON Payload to send:"; echo "$JSON_PAYLOAD"
|
|
||||||
|
|
||||||
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 "
|
|
||||||
--- HTTP STATUS: $HTTP_STATUS ---"
|
|
||||||
echo "--- RAW RESPONSE: ---"
|
|
||||||
cat "$TMP_FILE"
|
|
||||||
|
|
||||||
if [[ "$HTTP_STATUS" != "200" && "$HTTP_STATUS" != "201" ]]; then
|
|
||||||
echo -e "
|
|
||||||
❌ Failed to provision VPS (HTTP $HTTP_STATUS)"
|
|
||||||
jq . "$TMP_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "
|
|
||||||
✅ VPS provisioned:"
|
|
||||||
IP=$(jq -r '.ipv4[0]' "$TMP_FILE")
|
|
||||||
echo "Label: $LABEL"
|
|
||||||
echo "IP Address: $IP"
|
|
||||||
echo "Root Password: $ROOT_PASS"
|
|
||||||
rm "$TMP_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 -p "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
|
fi
|
||||||
|
echo "...still waiting ($i/10)..."
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
echo "❌ DNS A record for $HOSTNAME did not propagate in time. Skipping rDNS setup."
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -153,6 +72,30 @@ case "$1" in
|
|||||||
mastodon)
|
mastodon)
|
||||||
provision_vps "$2" "$PACKAGE_MASTODON_REGION" "$PACKAGE_MASTODON_TYPE" "$PACKAGE_MASTODON_IMAGE" "$3"
|
provision_vps "$2" "$PACKAGE_MASTODON_REGION" "$PACKAGE_MASTODON_TYPE" "$PACKAGE_MASTODON_IMAGE" "$3"
|
||||||
;;
|
;;
|
||||||
|
backup)
|
||||||
|
enable_backups_by_label "$2"
|
||||||
|
;;
|
||||||
|
disable-backup)
|
||||||
|
disable_backups_by_label "$2"
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_vps "$2"
|
||||||
|
;;
|
||||||
|
listvps)
|
||||||
|
list_all_vps
|
||||||
|
;;
|
||||||
|
disable)
|
||||||
|
disable_ip "$2"
|
||||||
|
;;
|
||||||
|
resize)
|
||||||
|
resize_vps "$2" "$3"
|
||||||
|
;;
|
||||||
|
safe-create)
|
||||||
|
safe_create_dataset "$2" "$3"
|
||||||
|
;;
|
||||||
|
verify_ptr)
|
||||||
|
verify_ptr "$2"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Usage: $0 <command> [...]"
|
echo "Usage: $0 <command> [...]"
|
||||||
echo "Available commands: provision, reboot, destroy, safe, ultra, micro, mastodon"
|
echo "Available commands: provision, reboot, destroy, safe, ultra, micro, mastodon"
|
||||||
|
@ -1,270 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# genesisctl - Genesis VPS Provisioning and Reboot CLI
|
|
||||||
# Usage:
|
|
||||||
# genesisctl provision <label> <region> <type> <image> [root_pass]
|
|
||||||
# genesisctl reboot <linode-id>
|
|
||||||
# genesisctl list regions|types|images
|
|
||||||
# genesisctl ultra <label> [root_pass]
|
|
||||||
# genesisctl safe <label> [root_pass]
|
|
||||||
# genesisctl micro <label> [root_pass]
|
|
||||||
# genesisctl mastodon <label> [root_pass]
|
|
||||||
# genesisctl destroy <label>
|
|
||||||
|
|
||||||
LINODE_API_TOKEN="f8b1552bf1f2f791e16fed0c1474d56014330de1c33810527523e44a7389cb6f"
|
|
||||||
|
|
||||||
# Package presets
|
|
||||||
PACKAGE_ULTRA_REGION="us-east"
|
|
||||||
PACKAGE_ULTRA_TYPE="g6-dedicated-4"
|
|
||||||
PACKAGE_ULTRA_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_SAFE_REGION="us-east"
|
|
||||||
PACKAGE_SAFE_TYPE="g6-standard-2"
|
|
||||||
PACKAGE_SAFE_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_MICRO_REGION="us-east"
|
|
||||||
PACKAGE_MICRO_TYPE="g6-nanode-1"
|
|
||||||
PACKAGE_MICRO_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_MASTODON_REGION="us-east"
|
|
||||||
PACKAGE_MASTODON_TYPE="g6-standard-4"
|
|
||||||
PACKAGE_MASTODON_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
USER_DATA=$(echo "#cloud-config
|
|
||||||
packages:
|
|
||||||
- rsync
|
|
||||||
- fail2ban
|
|
||||||
runcmd:
|
|
||||||
- curl -s https://help.sshjunkie.com/scripts/genesis-bootstrap.sh | bash" | base64 -w 0)
|
|
||||||
|
|
||||||
echo "Provisioning VPS '$LABEL' in $REGION with type $TYPE and image $IMAGE..."
|
|
||||||
echo "[DEBUG] Using API token prefix: ${LINODE_API_TOKEN:0:8}********"
|
|
||||||
echo "[DEBUG] JSON Payload to send:"; echo "$JSON_PAYLOAD"
|
|
||||||
|
|
||||||
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 "
|
|
||||||
--- HTTP STATUS: $HTTP_STATUS ---"
|
|
||||||
echo "--- RAW RESPONSE: ---"
|
|
||||||
cat "$TMP_FILE"
|
|
||||||
|
|
||||||
if [[ "$HTTP_STATUS" != "200" && "$HTTP_STATUS" != "201" ]]; then
|
|
||||||
echo -e "
|
|
||||||
❌ Failed to provision VPS (HTTP $HTTP_STATUS)"
|
|
||||||
jq . "$TMP_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "
|
|
||||||
✅ VPS provisioned:"
|
|
||||||
IP=$(jq -r '.ipv4[0]' "$TMP_FILE")
|
|
||||||
echo "Label: $LABEL"
|
|
||||||
echo "IP Address: $IP"
|
|
||||||
echo "Root Password: $ROOT_PASS"
|
|
||||||
rm "$TMP_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 -p "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
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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)"'
|
|
||||||
}
|
|
||||||
|
|
||||||
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 }'
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
provision)
|
|
||||||
provision_vps "$2" "$3" "$4" "$5" "$6"
|
|
||||||
;;
|
|
||||||
reboot)
|
|
||||||
reboot_vps "$2"
|
|
||||||
;;
|
|
||||||
destroy)
|
|
||||||
destroy_vps_by_label "$2"
|
|
||||||
;;
|
|
||||||
safe)
|
|
||||||
provision_vps "$2" "$PACKAGE_SAFE_REGION" "$PACKAGE_SAFE_TYPE" "$PACKAGE_SAFE_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
ultra)
|
|
||||||
provision_vps "$2" "$PACKAGE_ULTRA_REGION" "$PACKAGE_ULTRA_TYPE" "$PACKAGE_ULTRA_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
micro)
|
|
||||||
provision_vps "$2" "$PACKAGE_MICRO_REGION" "$PACKAGE_MICRO_TYPE" "$PACKAGE_MICRO_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
mastodon)
|
|
||||||
provision_vps "$2" "$PACKAGE_MASTODON_REGION" "$PACKAGE_MASTODON_TYPE" "$PACKAGE_MASTODON_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
backup)
|
|
||||||
enable_backups_by_label "$2"
|
|
||||||
;;
|
|
||||||
disable-backup)
|
|
||||||
disable_backups_by_label "$2"
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status_vps "$2"
|
|
||||||
;;
|
|
||||||
listvps)
|
|
||||||
list_all_vps
|
|
||||||
;;
|
|
||||||
resize)
|
|
||||||
resize_vps "$2" "$3"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 <command> [...]"
|
|
||||||
echo "Available commands: provision, reboot, destroy, safe, ultra, micro, mastodon"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
@ -1,288 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# genesisctl - Genesis VPS Provisioning and Reboot CLI
|
|
||||||
# Usage:
|
|
||||||
# genesisctl provision <label> <region> <type> <image> [root_pass]
|
|
||||||
# genesisctl reboot <linode-id>
|
|
||||||
# genesisctl list regions|types|images
|
|
||||||
# genesisctl ultra <label> [root_pass]
|
|
||||||
# genesisctl safe <label> [root_pass]
|
|
||||||
# genesisctl micro <label> [root_pass]
|
|
||||||
# genesisctl mastodon <label> [root_pass]
|
|
||||||
# genesisctl destroy <label>
|
|
||||||
|
|
||||||
LINODE_API_TOKEN="f8b1552bf1f2f791e16fed0c1474d56014330de1c33810527523e44a7389cb6f"
|
|
||||||
|
|
||||||
# Package presets
|
|
||||||
PACKAGE_ULTRA_REGION="us-east"
|
|
||||||
PACKAGE_ULTRA_TYPE="g6-dedicated-4"
|
|
||||||
PACKAGE_ULTRA_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_SAFE_REGION="us-east"
|
|
||||||
PACKAGE_SAFE_TYPE="g6-standard-2"
|
|
||||||
PACKAGE_SAFE_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_MICRO_REGION="us-east"
|
|
||||||
PACKAGE_MICRO_TYPE="g6-nanode-1"
|
|
||||||
PACKAGE_MICRO_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
PACKAGE_MASTODON_REGION="us-east"
|
|
||||||
PACKAGE_MASTODON_TYPE="g6-standard-4"
|
|
||||||
PACKAGE_MASTODON_IMAGE="linode/ubuntu22.04"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
USER_DATA=$(echo "#cloud-config
|
|
||||||
packages:
|
|
||||||
- rsync
|
|
||||||
- fail2ban
|
|
||||||
runcmd:
|
|
||||||
- curl -s https://help.sshjunkie.com/scripts/genesis-bootstrap.sh | bash" | base64 -w 0)
|
|
||||||
|
|
||||||
echo "Provisioning VPS '$LABEL' in $REGION with type $TYPE and image $IMAGE..."
|
|
||||||
echo "[DEBUG] Using API token prefix: ${LINODE_API_TOKEN:0:8}********"
|
|
||||||
echo "[DEBUG] JSON Payload to send:"; echo "$JSON_PAYLOAD"
|
|
||||||
|
|
||||||
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 "
|
|
||||||
--- HTTP STATUS: $HTTP_STATUS ---"
|
|
||||||
echo "--- RAW RESPONSE: ---"
|
|
||||||
cat "$TMP_FILE"
|
|
||||||
|
|
||||||
if [[ "$HTTP_STATUS" != "200" && "$HTTP_STATUS" != "201" ]]; then
|
|
||||||
echo -e "
|
|
||||||
❌ Failed to provision VPS (HTTP $HTTP_STATUS)"
|
|
||||||
jq . "$TMP_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "
|
|
||||||
✅ VPS provisioned:"
|
|
||||||
IP=$(jq -r '.ipv4[0]' "$TMP_FILE")
|
|
||||||
echo "Label: $LABEL"
|
|
||||||
echo "IP Address: $IP"
|
|
||||||
echo "Root Password: $ROOT_PASS"
|
|
||||||
rm "$TMP_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 -p "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
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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)"'
|
|
||||||
}
|
|
||||||
|
|
||||||
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 }'
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
case "$1" in
|
|
||||||
provision)
|
|
||||||
provision_vps "$2" "$3" "$4" "$5" "$6"
|
|
||||||
;;
|
|
||||||
reboot)
|
|
||||||
reboot_vps "$2"
|
|
||||||
;;
|
|
||||||
destroy)
|
|
||||||
destroy_vps_by_label "$2"
|
|
||||||
;;
|
|
||||||
safe)
|
|
||||||
provision_vps "$2" "$PACKAGE_SAFE_REGION" "$PACKAGE_SAFE_TYPE" "$PACKAGE_SAFE_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
ultra)
|
|
||||||
provision_vps "$2" "$PACKAGE_ULTRA_REGION" "$PACKAGE_ULTRA_TYPE" "$PACKAGE_ULTRA_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
micro)
|
|
||||||
provision_vps "$2" "$PACKAGE_MICRO_REGION" "$PACKAGE_MICRO_TYPE" "$PACKAGE_MICRO_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
mastodon)
|
|
||||||
provision_vps "$2" "$PACKAGE_MASTODON_REGION" "$PACKAGE_MASTODON_TYPE" "$PACKAGE_MASTODON_IMAGE" "$3"
|
|
||||||
;;
|
|
||||||
backup)
|
|
||||||
enable_backups_by_label "$2"
|
|
||||||
;;
|
|
||||||
disable-backup)
|
|
||||||
disable_backups_by_label "$2"
|
|
||||||
;;
|
|
||||||
status)
|
|
||||||
status_vps "$2"
|
|
||||||
;;
|
|
||||||
listvps)
|
|
||||||
list_all_vps
|
|
||||||
;;
|
|
||||||
resize)
|
|
||||||
resize_vps "$2" "$3"
|
|
||||||
;;
|
|
||||||
safe-create)
|
|
||||||
safe_create_dataset "$2" "$3"
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
|
||||||
*)
|
|
||||||
echo "Usage: $0 <command> [...]"
|
|
||||||
echo "Available commands: provision, reboot, destroy, safe, ultra, micro, mastodon"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
@ -70,7 +70,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gog": {
|
"gog": {
|
||||||
"recording": false,
|
"recording": true,
|
||||||
"duration": 10800,
|
"duration": 10800,
|
||||||
"schedule": [
|
"schedule": [
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user