tailscale-toggle.sh hinzugefügt
This commit is contained in:
147
tailscale-toggle.sh
Normal file
147
tailscale-toggle.sh
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# ts-menu.sh — Interaktives Menü zum Sperren/Entsperren von Tailscale-Geräten
|
||||||
|
#
|
||||||
|
# Nutzt `gum` für die Menüführung und die Tailscale-API (authorized-Flag),
|
||||||
|
# um einzelne Geräte vom Tailnet zu trennen bzw. wieder zuzulassen.
|
||||||
|
#
|
||||||
|
# Voraussetzungen:
|
||||||
|
# - gum (https://github.com/charmbracelet/gum)
|
||||||
|
# - curl, jq
|
||||||
|
# - API-Access-Token: https://login.tailscale.com/admin/settings/keys
|
||||||
|
#
|
||||||
|
# Konfiguration in ~/.ts-toggle.conf:
|
||||||
|
# TAILSCALE_API_KEY="tskey-api-xxxxx"
|
||||||
|
# TAILSCALE_TAILNET="deine-org.ts.net"
|
||||||
|
#
|
||||||
|
# Verwendung:
|
||||||
|
# ./ts-menu.sh
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONF_FILE="${HOME}/.ts-toggle.conf"
|
||||||
|
[[ -f "$CONF_FILE" ]] && source "$CONF_FILE"
|
||||||
|
|
||||||
|
API_KEY="${TAILSCALE_API_KEY:-}"
|
||||||
|
TAILNET="${TAILSCALE_TAILNET:-}"
|
||||||
|
API_BASE="https://api.tailscale.com/api/v2"
|
||||||
|
|
||||||
|
die() {
|
||||||
|
gum style --foreground 1 "Fehler: $*" >&2 2>/dev/null || echo "Fehler: $*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
command -v gum >/dev/null 2>&1 || { echo "Fehler: gum wird benötigt (https://github.com/charmbracelet/gum)."; exit 1; }
|
||||||
|
command -v curl >/dev/null 2>&1 || die "curl wird benötigt."
|
||||||
|
command -v jq >/dev/null 2>&1 || die "jq wird benötigt."
|
||||||
|
[[ -n "$API_KEY" ]] || die "TAILSCALE_API_KEY ist nicht gesetzt (siehe ~/.ts-toggle.conf)."
|
||||||
|
[[ -n "$TAILNET" ]] || die "TAILSCALE_TAILNET ist nicht gesetzt (siehe ~/.ts-toggle.conf)."
|
||||||
|
|
||||||
|
api_get() {
|
||||||
|
curl -sf -u "${API_KEY}:" "${API_BASE}/$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
api_post() {
|
||||||
|
curl -sf -u "${API_KEY}:" -H "Content-Type: application/json" \
|
||||||
|
-X POST -d "$2" "${API_BASE}/$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_devices() {
|
||||||
|
api_get "tailnet/${TAILNET}/devices"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Zeigt eine Auswahlliste der Geräte und gibt die gewählte Device-ID aus
|
||||||
|
choose_device() {
|
||||||
|
local devices="$1"
|
||||||
|
local selection id
|
||||||
|
|
||||||
|
selection=$(echo "$devices" | jq -r '
|
||||||
|
.devices[] | [.id, .name, (if .authorized then "✅ aktiv" else "⛔ gesperrt" end)] | @tsv' \
|
||||||
|
| awk -F'\t' '{printf "%-35s %s\n", $2, $3, $1}' \
|
||||||
|
| gum choose --header "Gerät auswählen" --height 15) || return 1
|
||||||
|
|
||||||
|
[[ -z "$selection" ]] && return 1
|
||||||
|
|
||||||
|
local name
|
||||||
|
name=$(echo "$selection" | awk '{print $1}')
|
||||||
|
id=$(echo "$devices" | jq -r --arg n "$name" '.devices[] | select(.name==$n) | .id')
|
||||||
|
echo "$id"
|
||||||
|
}
|
||||||
|
|
||||||
|
action_toggle() {
|
||||||
|
local devices id name authorized new_value action_label
|
||||||
|
|
||||||
|
devices=$(gum spin --title "Lade Geräteliste..." --show-output -- bash -c "curl -sf -u '${API_KEY}:' '${API_BASE}/tailnet/${TAILNET}/devices'") \
|
||||||
|
|| { gum style --foreground 1 "Konnte Geräteliste nicht laden."; return; }
|
||||||
|
|
||||||
|
id=$(choose_device "$devices") || return
|
||||||
|
[[ -z "$id" ]] && return
|
||||||
|
|
||||||
|
name=$(echo "$devices" | jq -r --arg id "$id" '.devices[] | select(.id==$id) | .name')
|
||||||
|
authorized=$(echo "$devices" | jq -r --arg id "$id" '.devices[] | select(.id==$id) | .authorized')
|
||||||
|
|
||||||
|
if [[ "$authorized" == "true" ]]; then
|
||||||
|
new_value="false"
|
||||||
|
action_label="sperren"
|
||||||
|
else
|
||||||
|
new_value="true"
|
||||||
|
action_label="entsperren"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if gum confirm "Gerät '$name' jetzt $action_label?"; then
|
||||||
|
gum spin --title "Setze Status..." -- \
|
||||||
|
curl -sf -u "${API_KEY}:" -H "Content-Type: application/json" \
|
||||||
|
-X POST -d "{\"authorized\": ${new_value}}" \
|
||||||
|
"${API_BASE}/device/${id}/authorized" >/dev/null
|
||||||
|
|
||||||
|
if [[ "$new_value" == "false" ]]; then
|
||||||
|
gum style --foreground 1 --bold "⛔ '$name' wurde gesperrt."
|
||||||
|
else
|
||||||
|
gum style --foreground 2 --bold "✅ '$name' wurde entsperrt."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
gum style --foreground 3 "Abgebrochen."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
action_list() {
|
||||||
|
local devices
|
||||||
|
devices=$(gum spin --title "Lade Geräteliste..." --show-output -- bash -c "curl -sf -u '${API_KEY}:' '${API_BASE}/tailnet/${TAILNET}/devices'") \
|
||||||
|
|| { gum style --foreground 1 "Konnte Geräteliste nicht laden."; return; }
|
||||||
|
|
||||||
|
echo "$devices" | jq -r '
|
||||||
|
["NAME","OS","STATUS"],
|
||||||
|
(.devices[] | [.name, .os, (if .authorized then "aktiv" else "GESPERRT" end)])
|
||||||
|
| @tsv' | gum table --separator=$'\t' --columns "Name,OS,Status"
|
||||||
|
}
|
||||||
|
|
||||||
|
main_menu() {
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
gum style --border rounded --padding "1 2" --border-foreground 6 \
|
||||||
|
"🔒 Tailscale Geräteverwaltung — Tailnet: ${TAILNET}"
|
||||||
|
|
||||||
|
choice=$(gum choose \
|
||||||
|
"Gerät sperren/entsperren" \
|
||||||
|
"Geräteliste anzeigen" \
|
||||||
|
"Beenden" \
|
||||||
|
--header "Was möchtest du tun?")
|
||||||
|
|
||||||
|
case "$choice" in
|
||||||
|
"Gerät sperren/entsperren")
|
||||||
|
action_toggle
|
||||||
|
gum input --placeholder "Enter drücken zum Fortfahren..." >/dev/null || true
|
||||||
|
;;
|
||||||
|
"Geräteliste anzeigen")
|
||||||
|
action_list
|
||||||
|
gum input --placeholder "Enter drücken zum Fortfahren..." >/dev/null || true
|
||||||
|
;;
|
||||||
|
"Beenden"|"")
|
||||||
|
echo "Bis dann 👋"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
main_menu
|
||||||
Reference in New Issue
Block a user