Created
May 4, 2022 21:28
-
-
Save brettowe/592d2652acc7a3277678a576eef53c0b to your computer and use it in GitHub Desktop.
script that uses a yubikey on headless machines without a power button to initialize shutdown
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# this script listens for a yubikey to be plugged into a device and shutdown if the button is pressed on it | |
# handy for headless devices that have no power buttons but need to be shutdown | |
# no verification is done to see if its an authorized yubi key | |
# changelog | |
# 20220504 - initial release | |
import functools | |
import os.path | |
import pyudev | |
import subprocess | |
from fido2.hid import CtapHidDevice | |
from fido2.client import Fido2Client | |
from fido2.server import Fido2Server | |
try: | |
from fido2.pcsc import CtapPcscDevice | |
except ImportError: | |
CtapPcscDevice = None | |
def touchKey(): | |
uv = "discouraged" | |
dev = next(CtapHidDevice.list_devices(), None) | |
# Set up a FIDO 2 client using the origin https://example.com | |
client = Fido2Client(dev, "https://localhost.com") | |
# Prefer UV if supported and configured | |
if client.info.options.get("uv"): | |
uv = "preferred" | |
print("Authenticator supports User Verification") | |
server = Fido2Server({"id": "localhost.com", "name": "Fido Shutdown"}, attestation="direct") | |
user = {"id": b"user_id", "name": "A. User"} | |
# Prepare parameters for makeCredential | |
create_options, state = server.register_begin( | |
user, user_verification=uv, authenticator_attachment="cross-platform" | |
) | |
try: | |
# Create a credential | |
# (really just touch the device we don't care beyond that) | |
result = client.make_credential(create_options["publicKey"]) | |
# touched | |
return True | |
except: | |
# no touch or removed | |
return False | |
def main(): | |
BASE_PATH = os.path.abspath(os.path.dirname(__file__)) | |
path = functools.partial(os.path.join, BASE_PATH) | |
call = lambda x, *args: subprocess.call([path(x)] + list(args)) | |
context = pyudev.Context() | |
monitor = pyudev.Monitor.from_netlink(context) | |
monitor.filter_by(subsystem='usb') # Remove this line to listen for all devices. | |
monitor.start() | |
for device in iter(monitor.poll, None): | |
if device.get('ID_MODEL_ID') == '0407' and device.get('ID_VENDOR_ID') == '1050': | |
try: | |
l_status = touchKey() | |
except: | |
l_status = False | |
if l_status: | |
print ("shutdown") | |
subprocess.run(["/usr/bin/shutdown", "-h", "now"]) | |
else: | |
print ("no shutdown") | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment