Odyssey
certificates.py
1 #!/usr/bin/env python
2 """
3 Run cumanage operations in an AWS hosted stack.
4 """
5 
6 import json
7 import sys
8 import shellish
9 import boto3
10 from . import base
11 from Crypto.Cipher import AES
12 from Crypto.Protocol.KDF import PBKDF2
13 from Crypto.Util import Counter
14 from pwgen import pwgen
15 
16 AWS_REGIONS = [
17  'us-east-2',
18  'us-west-2',
19 ]
20 SALT = 'LTzkm1w/p3ReDm9kmfmnwQ=='
21 ENCODING = 'utf-8'
22 AES_KEY_BYTES = 32
23 
24 
25 def __derive_key__(password, salt=None):
26  '''Return key using PBKDF2'''
27  if not salt:
28  salt = SALT
29  return PBKDF2(
30  password,
31  salt.encode(ENCODING),
32  AES_KEY_BYTES
33  )
34 
35 
36 def __encrypt_data__(password, salt, data):
37  '''Encrypt given data using password and PBKDF2 key stretching'''
38  key = __derive_key__(password, salt)
39  aes_cipher = AES.new(key, AES.MODE_CTR, counter=Counter.new(128))
40  return aes_cipher.encrypt(data.encode(ENCODING))
41 
42 
44  '''Manage Client Certificates'''
45  name = 'certificate'
46 
47  def setup_args(self, parser):
48  self.add_subcommand(UploadCertificateCommand)
49 
50 
52  '''Upload Client Certificates to AWS'''
53 
54  name = 'upload'
55 
56  def setup_args(self, parser):
57  self.add_argument('--secret-id', action='store', required=True,
58  help='Path for secret storage')
59  self.add_argument('--certificate-file', action='store',
60  help='Certificate file to encrypt and upload')
61  self.add_argument('--output', '-o', action='store',
62  help='Name of output file if specified')
63 
64  def run(self, args):
65 
66  def read_file(filename):
67  with open(filename, 'r') as fh:
68  return fh.read()
69 
70  secret_id = args.secret_id
71  if args.certificate_file:
72  certificate = read_file(args.certificate_file)
73  else:
74  certificate = sys.stdin.read()
75  salt = pwgen(16)
76  private_key = pwgen(50)
77 
78  encrypted_certificate = __encrypt_data__(private_key,
79  salt,
80  certificate)
81 
82  if args.output:
83  with open(args.output, 'wb') as output:
84  output.write(encrypted_certificate)
85  else:
86  print(encrypted_certificate)
87 
88  shellish.vtmlprint(
89  '<green>Encrypted and Uploaded Certificate</green>',
90  file=sys.stderr
91  )
92 
93  secret_arns = update_secrets_value(secret_id, salt, private_key)
94 
95  shellish.vtmlprint(
96  '<green>Created/Updated Encryption Keys: %s</green>' % (
97  ', '.join(secret_arns)),
98  file=sys.stderr
99  )
100 
101 
102 def update_secrets_value(secret_id, salt, private_key):
103  '''Update new secrets value for `secret_id`'''
104  arns = []
105  for region in AWS_REGIONS:
106  client = boto3.client('secretsmanager', region_name=region)
107  secret_dictionary = get_secret_value(client, secret_id)
108 
109  secret_dictionary['salt'] = salt
110  secret_dictionary['password'] = private_key
111 
112  arn = put_secret_value(client, secret_id, secret_dictionary)
113  arns.append(arn)
114 
115  return arns
116 
117 
118 def get_secret_value(client, secret_id):
119  '''Return the JSON data for the provided `secret_id`
120 
121  If there is no secret for the provided `secret_id` return an empty
122  dictionary.
123  '''
124  try:
125  response = client.get_secret_value(SecretId=secret_id)
126  return json.loads(response['SecretString'])
127  except client.exceptions.ResourceNotFoundException:
128  return {}
129 
130 
131 def put_secret_value(client, secret_id, secrets):
132  '''Create or Update secret value of the provided `secret_id`'''
133  json_secrets = json.dumps(secrets)
134  if get_secret_value(client, secret_id) == {}:
135  response = client.create_secret(
136  Name=secret_id,
137  SecretString=json_secrets
138  )
139  else:
140  response = client.put_secret_value(
141  SecretId=secret_id,
142  SecretString=json.dumps(secrets)
143  )
144  return response['ARN']
def get_secret_value(client, secret_id)
def __derive_key__(password, salt=None)
Definition: certificates.py:25
def __encrypt_data__(password, salt, data)
Definition: certificates.py:36
def put_secret_value(client, secret_id, secrets)
def update_secrets_value(secret_id, salt, private_key)