| ''' |
| Copyright 2019 Google LLC |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| https://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ''' |
| |
| |
| import os |
| import platform |
| import shutil |
| import subprocess |
| import sys |
| |
| import paramiko |
| from paramiko.ssh_exception import SSHException, PasswordRequiredException |
| from paramiko.rsakey import RSAKey |
| |
| from mdt import config |
| |
| |
| SUPPORTED_SYSTEMS = [ |
| 'Linux', |
| 'MacOS', |
| 'BSD', |
| ] |
| |
| KEYSDIR = os.path.join(config.CONFIG_BASEDIR, "keys") |
| KEYFILE_PATH = os.path.join(config.CONFIG_BASEDIR, "keys", "mdt.key") |
| |
| |
| class Keystore: |
| def __init__(self): |
| if not os.path.exists(config.CONFIG_BASEDIR): |
| os.makedirs(CONFIG_BASEDIR, mode=0o700) |
| if not os.path.exists(KEYSDIR): |
| os.makedirs(KEYSDIR, mode=0o700) |
| if not os.path.exists(KEYFILE_PATH): |
| self.pkey = None |
| else: |
| try: |
| self.pkey = RSAKey.from_private_key_file(KEYFILE_PATH) |
| except IOError as e: |
| print("Unable to read private key from file: {0}".format(e)) |
| sys.exit(1) |
| except PasswordRequiredException as e: |
| print("Unable to load in private key: {0}".format(e)) |
| sys.exit(1) |
| |
| def generateKey(self): |
| self.pkey = RSAKey.generate(bits=4096) |
| |
| try: |
| self.pkey.write_private_key_file(KEYFILE_PATH) |
| except IOError as e: |
| print("Unable to write private key to disk: {0}".format(e)) |
| return False |
| else: |
| return True |
| |
| def importKey(self, keyfile): |
| try: |
| self.pkey = RSAKey.from_private_key_file(keyfile) |
| except IOError as e: |
| print("Unable to read private key from file: {0}".format(e)) |
| return False |
| except PasswordRequiredException as e: |
| print("Unable to load in private key: {0}".format(e)) |
| return False |
| except SSHException as e: |
| print("Unable to import private key: {0}".format(e)) |
| print("Note: Only OpenSSH keys generated using ssh-keygen in PEM format are supported.") |
| return False |
| |
| try: |
| self.pkey.write_private_key_file(KEYFILE_PATH) |
| except IOError as e: |
| print("Unable to write private key to disk: {0}".format(e)) |
| return False |
| else: |
| return True |
| |
| def key(self): |
| return self.pkey |
| |
| |
| class GenKeyCommand: |
| '''Usage: mdt genkey |
| |
| Generates an SSH key and stores it to disk. |
| |
| Note that this does not prompt if you want to replace an already existing |
| key and will happily overwrite without telling you! Also note, you should remove |
| the keys previously stored on the device in $HOME/.ssh/authorized_keys and |
| restart the mdt-keymaster service on the device to re-push any newly generated |
| keys. |
| ''' |
| |
| def run(self, args): |
| if os.path.exists(KEYFILE_PATH): |
| os.unlink(KEYFILE_PATH) |
| keystore = Keystore() |
| if not keystore.generateKey(): |
| return 1 |
| return 0 |
| |
| |
| class SetKeyCommand: |
| '''Usage: mdt setkey <path-to-private-key> |
| |
| Copies an SSH private key provided into the MDT key store for use with |
| authentication later.''' |
| |
| def run(self, args): |
| if len(args) != 2: |
| print("Usage: mdt setkey <path-to-private-key>") |
| return 1 |
| |
| source_keyfile = args[1] |
| if not os.path.exists(source_keyfile): |
| print("Can't copy {0}: no such file or directory.".format(source_keyfile)) |
| return 1 |
| |
| keystore = Keystore() |
| if not keystore.importKey(source_keyfile): |
| return 1 |
| |
| print("Key {0} imported.".format(source_keyfile)) |
| return 0 |