From 697dd9fcbfe800be3520fa9cb957c82a0c8746a1 Mon Sep 17 00:00:00 2001 From: Timothee 'TTimo' Besset <ttimo@ttimo.net> Date: Thu, 24 Feb 2022 14:53:17 -0600 Subject: [PATCH] add in service hooks for identify and registration --- hooks/approve-ssh-key | 63 +++++++++++++++++ hooks/devkit-1-identify | 151 ++++++++++++++++++++++++++++++++++++++++ hooks/install-ssh-key | 74 ++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100755 hooks/approve-ssh-key create mode 100755 hooks/devkit-1-identify create mode 100755 hooks/install-ssh-key diff --git a/hooks/approve-ssh-key b/hooks/approve-ssh-key new file mode 100755 index 0000000..8e93726 --- /dev/null +++ b/hooks/approve-ssh-key @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import sys +import os +import logging +import tempfile +from urllib.parse import quote_plus as urllib_quote_plus +import subprocess + +ENABLE_SSHD = '/usr/bin/steamos-polkit-helpers/steamos-enable-sshd' + +logger = logging.getLogger(os.path.realpath(__file__)) + +try: + import steam_devkit.utils +except: + sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + import steam_devkit.utils + +if __name__ == '__main__': + logger.setLevel(logging.INFO) + ch = logging.StreamHandler() + ch.setLevel(logging.INFO) + ch.setFormatter(logging.Formatter('%(name)s:%(message)s')) + logger.addHandler(ch) + + request = open(sys.argv[1], 'rt').read() + requestIP = 'Unknown' + try: + requestIP = sys.argv[2] + except NameError: + logger.debug('request IP not given as argument to hook') + pass + + if os.environ.get('DEVKIT_BYPASS_STEAM_PROMPT', None) == '1': + # Still using for the standalone dev-kit service application + logger.warning('Bypassing the Steam client approval - this is potentially insecure!') + MAGIC_PHRASE = '900b919520e4cf601998a71eec318fec' + tokens = request.split(' ') + if tokens[-1].strip('\n') != MAGIC_PHRASE: + raise Exception('Invalid request') + sys.exit(0) + + key_identifier = request.split(' ')[2] + request = f'Development host at IP {requestIP} key: {key_identifier!r}' + + steam_devkit.utils.validate_steam_client() + + with tempfile.TemporaryDirectory(prefix='approve-ssh-key') as tempdir: + logger.debug('Issue approve-ssh-key request to the running Steam client') + response = os.path.join(tempdir, 'response') + cmd = 'approve-ssh-key?response={}&request={}'.format( + urllib_quote_plus(response), + urllib_quote_plus(request), + ) + steam_devkit.utils.execute_steam_client_command(cmd) + with steam_devkit.utils.wait_on_file_response(response, timeout=30) as f: + logger.debug('Got response from Steam client') + # Make sure sshd is enabled to support development features + if os.path.exists(ENABLE_SSHD): + subprocess.check_call(ENABLE_SSHD) + else: + subprocess.check_call('sudo systemctl enable --now sshd', shell=True) diff --git a/hooks/devkit-1-identify b/hooks/devkit-1-identify new file mode 100755 index 0000000..301583d --- /dev/null +++ b/hooks/devkit-1-identify @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +# This file is part of steamos-devkit +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright 2017-2018 Collabora Ltd +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this package. If not, see +# <http://www.gnu.org/licenses/>. + +# Sample script run to identify the machine we are running on. This +# is used by the devkit daemon to choose an identity for the machine, +# and can also be run directly. +# +# A script or binary (written in any language) named devkit-1-identify +# can be placed in the same directory as this sample, and it will be +# used preferentially. +# +# Execution environment: +# - Runs as root, or as an ordinary user who can run games +# - Part of a non-interactive secure shell session +# Input: +# - None +# Output: +# - Exit 0 on success or nonzero on error +# - On success, must print JSON to stdout containing one object with +# the following keys and string values, all of which are optional: +# - "machine_id": The hexadecimal systemd/D-Bus machine-id(5) +# (in the case of a conflict between systemd's /etc/machine-id and +# D-Bus' traditional /var/lib/dbus/machine-id, the systemd version +# should be preferred) +# - "hostname": The machine-readable hostname as set by sethostname(2) +# - "pretty_hostname": A human-friendly version of the hostname as +# found in systemd's machine-info(5) +# - "product_family", "product_name", "product_serial", "product_uuid", +# "sys_vendor": as for /sys/devices/virtual/dmi/id +# - "product": The best human-friendly description of the machine +# hardware we can devise, for example "Alienware ASM100" +# - "serial": The best hardware serial number we can devise +# - "machine_name": The best unique name for the machine that we can +# devise +# - Any diagnostic messages must be printed to stderr only + +import json +import os +import re +import socket +import subprocess +import sys + +_MACHINE_ID_RE = re.compile(r'^[0-9A-Fa-f]{32,32}$') +_USELESS_HOSTNAMES = frozenset(( + 'debian', + 'host', + 'localhost', + 'steamos', + 'ubuntu', +)) +# The case combination sometimes varies, so this is lowercased and we +# use lower() to compare. +_USELESS_DMI_NAMES = frozenset(( + 'to be filled by o.e.m.', +)) + +if __name__ == '__main__': + + info = {} + steam_serialnumber = None + + for k in ('product_family', 'product_name', 'product_serial', + 'product_uuid', 'sys_vendor'): + try: + with open('/sys/devices/virtual/dmi/id/{}'.format(k)) as reader: + v = reader.read().strip() + + if v and v.lower() not in _USELESS_DMI_NAMES: + info[k] = v + except (IOError, OSError, UnicodeError): + pass + + try: + for f in ('/etc/machine-id', '/var/lib/dbus/machine-id'): + with open(f) as reader: + v = reader.read().strip() + + if _MACHINE_ID_RE.match(v): + info['machine_id'] = v + except (IOError, OSError, UnicodeError): + pass + + if 'product_family' in info and 'sys_vendor' in info: + info['product'] = '{sys_vendor} {product_family}'.format(**info) + elif 'product_name' in info and 'sys_vendor' in info: + info['product'] = '{sys_vendor} {product_name}'.format(**info) + elif 'product_family' in info: + info['product'] = info['product_family'] + elif 'product_name' in info: + info['product'] = info['product_name'] + + if os.access('/usr/bin/hostnamectl', os.X_OK): + try: + v = subprocess.check_output([ + 'hostnamectl', '--pretty', + ]).decode('utf-8').strip() + except (subprocess.CalledProcessError, UnicodeError): + pass + else: + if v: + info['pretty_hostname'] = v + + v = subprocess.check_output([ + 'hostnamectl', '--static', + ]).decode('utf-8').strip() + + if v: + info['hostname'] = v + + if 'hostname' not in info: + try: + info['hostname'] = socket.gethostname() + except (IOError, OSError, UnicodeError): + pass + + if steam_serialnumber: + info['serial'] = steam_serialnumber + elif 'product_serial' in info: + info['serial'] = info['product_serial'] + + if 'product' in info: + info['machine_name'] = info['product'] + + if 'hostname' in info and info['hostname'] not in _USELESS_HOSTNAMES: + info['machine_name'] = info['hostname'] + + if 'pretty_hostname' in info: + info['machine_name'] = info['pretty_hostname'] + + json.dump(info, sys.stdout, indent=4, sort_keys=True) + print() diff --git a/hooks/install-ssh-key b/hooks/install-ssh-key new file mode 100755 index 0000000..db89036 --- /dev/null +++ b/hooks/install-ssh-key @@ -0,0 +1,74 @@ +#!/bin/sh +# +# This file is part of steamos-devkit +# SPDX-License-Identifier: LGPL-2.1+ +# +# Copyright © 2017-2018 Collabora Ltd +# +# This package is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This package is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this package. If not, see +# <http://www.gnu.org/licenses/>. + +# Sample script to install ssh keys that were approved by the +# approve-ssh-key script. +# +# A script or binary (written in any language) named install-ssh-key +# can be placed in the same directory as this sample, and it will be +# used preferentially. +# +# Execution environment: +# - Runs as root +# Input: +# - The public key is in a temporary file accessible via argv[1], in +# OpenSSH format +# - argv[2], ... are the users to which we should grant access +# Output: +# - Exit 0 to accept the key, or nonzero to reject or on error +# +# This sample implementation should be suitable for any machine that +# has the specified users. A production implementation would be similar +# or even identical. + +set -e + +public_key="$1" +shift + +echo "Installing public key '$public_key' for users: $*" + +for user in "$@" +do + if pwent="$(getent passwd "$user")" + then + home="$(echo "$pwent" | cut -d: -f6)" + group="$(id -gn "$user")" + install -d -m 0755 -o "$user" -g "$group" "$home/.ssh" + if ! [ -e "$home/.ssh/authorized_keys" ] + then + install -m 0600 -o "$user" -g "$group" "$public_key" "$home/.ssh/authorized_keys" + else + if ! grep -qF "$(cut -d " " -f2 "$public_key")" "$home/.ssh/authorized_keys" + then + if [ -n "$(tail -1c "$home/.ssh/authorized_keys")" ] + then + echo >> "$home/.ssh/authorized_keys" + fi + cat "$public_key" >> "$home/.ssh/authorized_keys" + fi + fi + fi +done + +echo "Public key installed" + +exit 0 -- GitLab