diff --git a/bless/__about__.py b/bless/__about__.py index e8805d6..6d53e01 100644 --- a/bless/__about__.py +++ b/bless/__about__.py @@ -9,7 +9,7 @@ "sign SSH public keys.") __uri__ = "https://github.com/Netflix/bless" -__version__ = "0.3.0" +__version__ = "0.4.0" __author__ = "The BLESS developers" __email__ = "security@netflix.com" diff --git a/bless/request/bless_request_host.py b/bless/request/bless_request_host.py index 364316b..5729334 100644 --- a/bless/request/bless_request_host.py +++ b/bless/request/bless_request_host.py @@ -52,7 +52,7 @@ class BlessHostRequest: def __init__(self, hostnames, public_key_to_sign): """ A BlessRequest must have the following key value pairs to be valid. - :param hostnames: Comma-separated list of hostnames (s) to include in this host certificate. + :param hostnames: Comma-separated list of hostname(s) to include in this host certificate. :param public_key_to_sign: The id_XXX.pub that will be used in the SSH request. This is enforced in the issued certificate. """ self.hostnames = hostnames diff --git a/bless_client/bless_client_host.py b/bless_client/bless_client_host.py new file mode 100644 index 0000000..94654a0 --- /dev/null +++ b/bless_client/bless_client_host.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +"""bless_client +A sample client to invoke the BLESS Host SSH Cert Lambda function and save the signed SSH Certificate. + +Usage: + bless_client_host.py region lambda_function_name hostnames + + region: AWS region where your lambda is deployed. + + lambda_function_name: The AWS Lambda function's alias or ARN to invoke. + + hostnames: Comma-separated list of hostname(s) to include in this host certificate. + + id_rsa.pub to sign: The id_rsa.pub that will be used in the SSH request. This is + enforced in the issued certificate. + + output id_rsa-cert.pub: The file where the certificate should be saved. Per man SSH(1): + "ssh will also try to load certificate information from the filename + obtained by appending -cert.pub to identity filenames" e.g. the . +""" +import json +import os +import stat +import sys + +import boto3 + + +def main(argv): + if len(argv) != 5: + print( + 'Usage: bless_client_host.py region lambda_function_name hostnames ' + '') + print(len(argv)) + return -1 + + region, lambda_function_name, hostnames, public_key_filename, certificate_filename = argv + + with open(public_key_filename, 'r') as f: + public_key = f.read().strip() + + payload = {'hostnames': hostnames, 'public_key_to_sign': public_key} + + payload_json = json.dumps(payload) + + print('Executing:') + print('payload_json is: \'{}\''.format(payload_json)) + lambda_client = boto3.client('lambda', region_name=region) + response = lambda_client.invoke(FunctionName=lambda_function_name, + InvocationType='RequestResponse', LogType='None', + Payload=payload_json) + print('{}\n'.format(response['ResponseMetadata'])) + + if response['StatusCode'] != 200: + print('Error creating cert.') + return -1 + + payload = json.loads(response['Payload'].read()) + + if 'certificate' not in payload: + print(payload) + return -1 + + cert = payload['certificate'] + + with os.fdopen(os.open(certificate_filename, os.O_WRONLY | os.O_CREAT, 0o600), + 'w') as cert_file: + cert_file.write(cert) + + # If cert_file already existed with the incorrect permissions, fix them. + file_status = os.stat(certificate_filename) + if 0o600 != (file_status.st_mode & 0o777): + os.chmod(certificate_filename, stat.S_IRUSR | stat.S_IWUSR) + + print('Wrote Certificate to: ' + certificate_filename) + + +if __name__ == '__main__': + main(sys.argv[1:])