71 lines
3.6 KiB
Python
71 lines
3.6 KiB
Python
#!/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()
|