#!/usr/bin/env python # # gstfsd - WebVirtCloud daemon for managing VM's filesystem # import ipaddress import os import socketserver import json import re import socket import sys PORT = int(os.environ.get("GSTFSD_PORT", 16510)) ADDRESS = os.environ.get("GSTFSD_BIND_HOST", "127.0.0.1") def is_loopback(host): try: return ipaddress.ip_address(host).is_loopback except ValueError: return host == "localhost" # gstfsd is an unauthenticated root service that rewrites any VM's # /etc/shadow and authorized_keys, so it only listens on loopback unless # explicitly allowed (ROADMAP S-08). if not is_loopback(ADDRESS) and os.environ.get("GSTFSD_ALLOW_REMOTE") != "1": sys.stderr.write( "gstfsd: refusing to listen on %s (unauthenticated root service). " "Set GSTFSD_ALLOW_REMOTE=1 to allow it on a trusted network.\n" % ADDRESS ) sys.exit(1) import guestfs # noqa: E402 class MyTCPServer(socketserver.ThreadingTCPServer): allow_reuse_address = True address_family = socket.AF_INET6 if ":" in ADDRESS else socket.AF_INET class MyTCPServerHandler(socketserver.StreamRequestHandler): timeout = 30 # seconds; an idle client must not hold a thread forever def handle(self): # recive data d = self.request.recv(1024).strip() data = json.loads(d) # GuestFS gfs = guestfs.GuestFS(python_return_dict=True) try: gfs.add_domain(data['vname']) gfs.launch() parts = gfs.list_partitions() for part in parts: try: gfs.mount(part, '/') if gfs.is_file('/etc/shadow'): if data['action'] == 'password': file_shadow = gfs.cat('/etc/shadow') new_root_hash = "root:" + data['passwd'] + ":" file_shadow_new = re.sub('^root:.*?:', new_root_hash, file_shadow) gfs.write('/etc/shadow', file_shadow_new) gfs.chmod(0o640, '/etc/shadow') self.request.sendall(json.dumps({'return': 'success'}).encode()) if data['action'] == 'publickey': if not gfs.is_dir('/root/.ssh'): gfs.mkdir('/root/.ssh') gfs.chmod(0o700, "/root/.ssh") # Add the key; keep the ones already there. keys_path = '/root/.ssh/authorized_keys' keys = gfs.cat(keys_path) if gfs.is_file(keys_path) else '' new_key = data['key'].strip() if new_key not in keys.splitlines(): if keys and not keys.endswith('\n'): keys += '\n' gfs.write(keys_path, keys + new_key + '\n') gfs.chmod(0o600, '/root/.ssh/authorized_keys') self.request.sendall(json.dumps({'return': 'success'}).encode()) gfs.umount(part) except RuntimeError: pass gfs.shutdown() gfs.close() except Exception as err: self.request.sendall(bytes(json.dumps({'return': 'error', 'message': str(err)}).encode())) server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler) server.serve_forever()