Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions SSHKeyDistribut0r/command_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ def main():
print('Welcome to the world of key distribution!')
print()

parser = argparse.ArgumentParser(
description='A tool to automate key distribution with user authorization.')
parser = argparse.ArgumentParser(description='A tool to automate key distribution with user authorization.')
parser.add_argument('--dry-run', '-n', action='store_true',
help='show pending changes without applying them')
help='show pending changes without applying them')
parser.add_argument('--keys', '-k',
default='%s/%s/keys.yml' % (appdirs.user_config_dir(), prog),
help="path to keys file\n(default: '%(default)s')")
default='%s/%s/keys.yml' % (appdirs.user_config_dir(), prog),
help="path to keys file\n(default: '%(default)s')")
parser.add_argument('--server', '-s',
default='%s/%s/servers.yml' % (appdirs.user_config_dir(), prog),
help="path to server file (default: '%(default)s')")
default='%s/%s/servers.yml' % (appdirs.user_config_dir(), prog),
help="path to server file (default: '%(default)s')")
parser.add_argument('--export-csv-path', '-f',
help="path to csv export file (example: 'keys_distributor_result.csv')")
args = parser.parse_args()

try:
Expand Down
92 changes: 75 additions & 17 deletions SSHKeyDistribut0r/key_distribut0r.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import re
import socket
import sys
import yaml
import csv

import paramiko
import scp
import yaml

logging.raiseExceptions = False

Expand All @@ -26,6 +27,9 @@
YAML_EXT = re.compile("^\\.ya?ml$")
JSON_EXT = re.compile("^\\.json$")

ERROR_STATUS = 'Error'
SUCCESS_STATUS = 'Success'


def remove_special_chars(original_string):
return ''.join(e for e in original_string if e.isalnum())
Expand Down Expand Up @@ -63,10 +67,48 @@ def read_config(config_file):
sys.exit(1)


def get_maximum_column_lengths(messages):
column_count = len(messages[0])
column_max_lens = {i: max(len(message[i]) for message in messages) for i in range(column_count)}
return column_max_lens


def print_table_log(messages):

messages.sort(key=lambda m: m[0] == ERROR_STATUS)

max_column_lens = get_maximum_column_lengths(messages)

def print_borderline():
column_count = len(messages[0])
empty_columns = ('' for _ in range(column_count))
print("+{:-^{lens[0]}}+{:-^{lens[1]}}+{:-^{lens[2]}}+{:-^{lens[3]}}+".format(*empty_columns,
lens=max_column_lens))
print()
print_borderline()
for message in messages[1:]:
color_on = COLOR_RED if message[0] == ERROR_STATUS else COLOR_GREEN
clear_message = (re.sub(r'\s+', ' ', col) for col in message)
print("|{color_on}{:^{lens[0]}}{color_off}"
"|{:^{lens[1]}}|{:^{lens[2]}}"
"|{:^{lens[3]}}|".format(*clear_message, color_on=color_on, color_off=COLOR_END, lens=max_column_lens))
print_borderline()


def export_to_csv(path, messages):
try:
with open(path, 'w', encoding='utf-8') as file:
writer = csv.writer(file, delimiter='|')
writer.writerows(messages)
except OSError as e:
print(e)


def main(args):
# Load config files
servers = read_config(args.server)
keys = read_config(args.keys)
messages = [('Status', 'Ip', 'Comment', 'Description')]

for server in servers:
if server['authorized_users']:
Expand All @@ -85,7 +127,9 @@ def main(args):
key_stream.write('%s\n' % key)

if args.dry_run:
server_info_log(server['ip'], server['comment'], ', '.join(server_users))
msg = server['ip'], server['comment'], ', '.join(server_users)
server_info_log(*msg)
messages.append((SUCCESS_STATUS, *msg))
else:
# Configure SSH client
ssh_client = paramiko.SSHClient()
Expand All @@ -102,25 +146,39 @@ def main(args):
scp_client.putfo(key_stream, '.ssh/authorized_keys')

key_stream.close()
server_info_log(server['ip'], server['comment'], ', '.join(server_users))
msg = server['ip'], server['comment'], ', '.join(server_users)
server_info_log(*msg)
messages.append((SUCCESS_STATUS, *msg))

except paramiko.ssh_exception.PasswordRequiredException:
server_error_log(
server['ip'],
server['comment'],
'The private key file is protected by a passphrase, which is currently not supported.'
)
msg = server['ip'], \
server['comment'], \
'The private key file is protected by a passphrase, which is currently not supported.'
server_error_log(*msg)
messages.append((ERROR_STATUS, msg))
except paramiko.ssh_exception.AuthenticationException:
server_error_log(
server['ip'],
server['comment'],
'Cannot connect to server because of an authentication problem.'
)
msg = server['ip'],\
server['comment'], \
'Cannot connect to server because of an authentication problem.'
server_error_log(*msg)
messages.append((ERROR_STATUS, *msg))
except scp.SCPException:
server_error_log(server['ip'], server['comment'], 'Cannot send file to server.')
msg = server['ip'], server['comment'], 'Cannot send file to server.'
server_error_log(*msg)
messages.append((ERROR_STATUS, *msg))
except (paramiko.ssh_exception.NoValidConnectionsError, paramiko.ssh_exception.SSHException):
server_error_log(server['ip'], server['comment'], 'Cannot connect to server.')
msg = server['ip'], server['comment'], 'Cannot connect to server.'
server_error_log(*msg)
messages.append((ERROR_STATUS, *msg))
except socket.timeout:
server_error_log(server['ip'], server['comment'], 'Cannot connect to server because of a timeout.')
msg = server['ip'], server['comment'], 'Cannot connect to server because of a timeout.'
server_error_log(*msg)
messages.append((ERROR_STATUS, *msg))
else:
server_error_log(server['ip'], server['comment'], 'No user mentioned in configuration file!')
msg = server['ip'], server['comment'], 'No user mentioned in configuration file!'
server_error_log(*msg)
messages.append((ERROR_STATUS, *msg))
print_table_log(messages)
if args.export_csv_path:
export_to_csv(args.export_csv_path, messages)