Files

71 lines
3.6 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""SSH from the admin laptop to every machine on every campus.
python3 tools/fleet.py ard the command to paste into Apple Remote Desktop (Send UNIX Command, as root)
python3 tools/fleet.py config write ~/.ssh/config.d/waters from the machines that reported back
The ARD command, on each Mac: turns on Remote Login, lets the logged-in user's account take this
laptop's key (~/.ssh/id_ed25519_waters), and reports name, user and addresses to North Attleboro's
settings page (/api/fleet/hello). `config` turns that list into shortcuts: ssh na-tracks-mac.
"""
import json
import os
import re
import sys
import urllib.request
HUB = os.environ.get("FLEET_HUB", "http://10.1.1.1:8020")
KEY = os.path.expanduser("~/.ssh/id_ed25519_waters")
OUT = os.path.expanduser("~/.ssh/config.d/waters")
def pubkey():
return open(KEY + ".pub").read().strip()
def ard():
key = pubkey()
return r"""U=$(stat -f%Su /dev/console); if [ "$U" = root ] || [ -z "$U" ]; then echo "SKIPPED $(scutil --get ComputerName): nobody is logged in"; exit 0; fi
H=$(dscl . -read "/Users/$U" NFSHomeDirectory | awk '{print $2}'); KEY='""" + key + r"""'
mkdir -p "$H/.ssh" && touch "$H/.ssh/authorized_keys" && (grep -qF "$KEY" "$H/.ssh/authorized_keys" || echo "$KEY" >> "$H/.ssh/authorized_keys") && chown -R "$U:staff" "$H/.ssh" && chmod 700 "$H/.ssh" && chmod 600 "$H/.ssh/authorized_keys"
nc -z -G 2 127.0.0.1 22 >/dev/null 2>&1 || systemsetup -setremotelogin on >/dev/null 2>&1 || launchctl load -w /System/Library/LaunchDaemons/ssh.plist >/dev/null 2>&1
dseditgroup -o read com.apple.access_ssh >/dev/null 2>&1 && dseditgroup -o edit -a "$U" -t user com.apple.access_ssh >/dev/null 2>&1
IPS=$(ifconfig | awk '/inet /{print $2}' | grep -v '^127\.' | sed 's/.*/"&"/' | paste -sd, -); N=$(scutil --get ComputerName | tr -d '"\\')
curl -s -m 5 -X POST -H 'Content-Type: application/json' -d "{\"user\":\"$U\",\"name\":\"$N\",\"hostname\":\"$(scutil --get LocalHostName).local\",\"os\":\"mac\",\"ips\":[$IPS]}" """ + HUB + r"""/api/fleet/hello >/dev/null
echo "OK $N user=$U ssh=$(nc -z -G 2 127.0.0.1 22 >/dev/null 2>&1 && echo on || echo OFF)"
"""
def slug(s):
s = re.sub(r"\s*\(\d+\)$", "", s) # macOS's "Name (2)" after a rename clash
return re.sub(r"[^a-z0-9]+", "-", s.lower()).strip("-") or "mac"
def config():
with urllib.request.urlopen(HUB + "/api/fleet", timeout=10) as r:
machines = json.load(r)["machines"]
wired = lambda ip: not ip.startswith(("10.6.", "169.254.", "127."))
lines, used = ["# Written by tools/fleet.py from %s/api/fleet - every machine that took this laptop's key." % HUB], set()
for m in sorted(machines, key=lambda m: m["name"].lower()):
ip = next((i for i in m["ips"] if wired(i)), m["ips"][0] if m["ips"] else None)
if not ip:
continue
alias = slug(m["name"])
while alias in used:
alias += "-2"
used.add(alias)
lines += ["", "Host %s" % alias, " HostName %s" % ip, " User %s" % m["user"],
" IdentityFile %s" % KEY, " IdentitiesOnly yes", " ConnectTimeout 8"]
os.makedirs(os.path.dirname(OUT), exist_ok=True)
open(OUT, "w").write("\n".join(lines) + "\n")
cfg = os.path.expanduser("~/.ssh/config")
text = open(cfg).read() if os.path.exists(cfg) else ""
if "Include ~/.ssh/config.d/*" not in text:
open(cfg, "w").write("Include ~/.ssh/config.d/*\n\n" + text)
print("%d machines -> %s: %s" % (len(used), OUT, ", ".join(sorted(used))))
if __name__ == "__main__":
what = (sys.argv[1:] or ["ard"])[0]
print(ard()) if what == "ard" else config()