1# Copyright (c) 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved 2# 3# Permission is hereby granted, free of charge, to any person obtaining a 4# copy of this software and associated documentation files (the 5# "Software"), to deal in the Software without restriction, including 6# without limitation the rights to use, copy, modify, merge, publish, dis- 7# tribute, sublicense, and/or sell copies of the Software, and to permit 8# persons to whom the Software is furnished to do so, subject to the fol- 9# lowing conditions: 10# 11# The above copyright notice and this permission notice shall be included 12# in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- 16# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 17# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20# IN THE SOFTWARE. 21# 22 23import boto 24from boto.compat import json 25from boto.connection import AWSQueryConnection 26from boto.regioninfo import RegionInfo 27from boto.exception import JSONResponseError 28from boto.cloudhsm import exceptions 29 30 31class CloudHSMConnection(AWSQueryConnection): 32 """ 33 AWS CloudHSM Service 34 """ 35 APIVersion = "2014-05-30" 36 DefaultRegionName = "us-east-1" 37 DefaultRegionEndpoint = "cloudhsm.us-east-1.amazonaws.com" 38 ServiceName = "CloudHSM" 39 TargetPrefix = "CloudHsmFrontendService" 40 ResponseError = JSONResponseError 41 42 _faults = { 43 "InvalidRequestException": exceptions.InvalidRequestException, 44 "CloudHsmServiceException": exceptions.CloudHsmServiceException, 45 "CloudHsmInternalException": exceptions.CloudHsmInternalException, 46 } 47 48 49 def __init__(self, **kwargs): 50 region = kwargs.pop('region', None) 51 if not region: 52 region = RegionInfo(self, self.DefaultRegionName, 53 self.DefaultRegionEndpoint) 54 55 if 'host' not in kwargs or kwargs['host'] is None: 56 kwargs['host'] = region.endpoint 57 58 super(CloudHSMConnection, self).__init__(**kwargs) 59 self.region = region 60 61 def _required_auth_capability(self): 62 return ['hmac-v4'] 63 64 def create_hapg(self, label): 65 """ 66 Creates a high-availability partition group. A high- 67 availability partition group is a group of partitions that 68 spans multiple physical HSMs. 69 70 :type label: string 71 :param label: The label of the new high-availability partition group. 72 73 """ 74 params = {'Label': label, } 75 return self.make_request(action='CreateHapg', 76 body=json.dumps(params)) 77 78 def create_hsm(self, subnet_id, ssh_key, iam_role_arn, subscription_type, 79 eni_ip=None, external_id=None, client_token=None, 80 syslog_ip=None): 81 """ 82 Creates an uninitialized HSM instance. Running this command 83 provisions an HSM appliance and will result in charges to your 84 AWS account for the HSM. 85 86 :type subnet_id: string 87 :param subnet_id: The identifier of the subnet in your VPC in which to 88 place the HSM. 89 90 :type ssh_key: string 91 :param ssh_key: The SSH public key to install on the HSM. 92 93 :type eni_ip: string 94 :param eni_ip: The IP address to assign to the HSM's ENI. 95 96 :type iam_role_arn: string 97 :param iam_role_arn: The ARN of an IAM role to enable the AWS CloudHSM 98 service to allocate an ENI on your behalf. 99 100 :type external_id: string 101 :param external_id: The external ID from **IamRoleArn**, if present. 102 103 :type subscription_type: string 104 :param subscription_type: The subscription type. 105 106 :type client_token: string 107 :param client_token: A user-defined token to ensure idempotence. 108 Subsequent calls to this action with the same token will be 109 ignored. 110 111 :type syslog_ip: string 112 :param syslog_ip: The IP address for the syslog monitoring server. 113 114 """ 115 params = { 116 'SubnetId': subnet_id, 117 'SshKey': ssh_key, 118 'IamRoleArn': iam_role_arn, 119 'SubscriptionType': subscription_type, 120 } 121 if eni_ip is not None: 122 params['EniIp'] = eni_ip 123 if external_id is not None: 124 params['ExternalId'] = external_id 125 if client_token is not None: 126 params['ClientToken'] = client_token 127 if syslog_ip is not None: 128 params['SyslogIp'] = syslog_ip 129 return self.make_request(action='CreateHsm', 130 body=json.dumps(params)) 131 132 def create_luna_client(self, certificate, label=None): 133 """ 134 Creates an HSM client. 135 136 :type label: string 137 :param label: The label for the client. 138 139 :type certificate: string 140 :param certificate: The contents of a Base64-Encoded X.509 v3 141 certificate to be installed on the HSMs used by this client. 142 143 """ 144 params = {'Certificate': certificate, } 145 if label is not None: 146 params['Label'] = label 147 return self.make_request(action='CreateLunaClient', 148 body=json.dumps(params)) 149 150 def delete_hapg(self, hapg_arn): 151 """ 152 Deletes a high-availability partition group. 153 154 :type hapg_arn: string 155 :param hapg_arn: The ARN of the high-availability partition group to 156 delete. 157 158 """ 159 params = {'HapgArn': hapg_arn, } 160 return self.make_request(action='DeleteHapg', 161 body=json.dumps(params)) 162 163 def delete_hsm(self, hsm_arn): 164 """ 165 Deletes an HSM. Once complete, this operation cannot be undone 166 and your key material cannot be recovered. 167 168 :type hsm_arn: string 169 :param hsm_arn: The ARN of the HSM to delete. 170 171 """ 172 params = {'HsmArn': hsm_arn, } 173 return self.make_request(action='DeleteHsm', 174 body=json.dumps(params)) 175 176 def delete_luna_client(self, client_arn): 177 """ 178 Deletes a client. 179 180 :type client_arn: string 181 :param client_arn: The ARN of the client to delete. 182 183 """ 184 params = {'ClientArn': client_arn, } 185 return self.make_request(action='DeleteLunaClient', 186 body=json.dumps(params)) 187 188 def describe_hapg(self, hapg_arn): 189 """ 190 Retrieves information about a high-availability partition 191 group. 192 193 :type hapg_arn: string 194 :param hapg_arn: The ARN of the high-availability partition group to 195 describe. 196 197 """ 198 params = {'HapgArn': hapg_arn, } 199 return self.make_request(action='DescribeHapg', 200 body=json.dumps(params)) 201 202 def describe_hsm(self, hsm_arn=None, hsm_serial_number=None): 203 """ 204 Retrieves information about an HSM. You can identify the HSM 205 by its ARN or its serial number. 206 207 :type hsm_arn: string 208 :param hsm_arn: The ARN of the HSM. Either the HsmArn or the 209 SerialNumber parameter must be specified. 210 211 :type hsm_serial_number: string 212 :param hsm_serial_number: The serial number of the HSM. Either the 213 HsmArn or the HsmSerialNumber parameter must be specified. 214 215 """ 216 params = {} 217 if hsm_arn is not None: 218 params['HsmArn'] = hsm_arn 219 if hsm_serial_number is not None: 220 params['HsmSerialNumber'] = hsm_serial_number 221 return self.make_request(action='DescribeHsm', 222 body=json.dumps(params)) 223 224 def describe_luna_client(self, client_arn=None, 225 certificate_fingerprint=None): 226 """ 227 Retrieves information about an HSM client. 228 229 :type client_arn: string 230 :param client_arn: The ARN of the client. 231 232 :type certificate_fingerprint: string 233 :param certificate_fingerprint: The certificate fingerprint. 234 235 """ 236 params = {} 237 if client_arn is not None: 238 params['ClientArn'] = client_arn 239 if certificate_fingerprint is not None: 240 params['CertificateFingerprint'] = certificate_fingerprint 241 return self.make_request(action='DescribeLunaClient', 242 body=json.dumps(params)) 243 244 def get_config(self, client_arn, client_version, hapg_list): 245 """ 246 Gets the configuration files necessary to connect to all high 247 availability partition groups the client is associated with. 248 249 :type client_arn: string 250 :param client_arn: The ARN of the client. 251 252 :type client_version: string 253 :param client_version: The client version. 254 255 :type hapg_list: list 256 :param hapg_list: A list of ARNs that identify the high-availability 257 partition groups that are associated with the client. 258 259 """ 260 params = { 261 'ClientArn': client_arn, 262 'ClientVersion': client_version, 263 'HapgList': hapg_list, 264 } 265 return self.make_request(action='GetConfig', 266 body=json.dumps(params)) 267 268 def list_available_zones(self): 269 """ 270 Lists the Availability Zones that have available AWS CloudHSM 271 capacity. 272 273 274 """ 275 params = {} 276 return self.make_request(action='ListAvailableZones', 277 body=json.dumps(params)) 278 279 def list_hapgs(self, next_token=None): 280 """ 281 Lists the high-availability partition groups for the account. 282 283 This operation supports pagination with the use of the 284 NextToken member. If more results are available, the NextToken 285 member of the response contains a token that you pass in the 286 next call to ListHapgs to retrieve the next set of items. 287 288 :type next_token: string 289 :param next_token: The NextToken value from a previous call to 290 ListHapgs. Pass null if this is the first call. 291 292 """ 293 params = {} 294 if next_token is not None: 295 params['NextToken'] = next_token 296 return self.make_request(action='ListHapgs', 297 body=json.dumps(params)) 298 299 def list_hsms(self, next_token=None): 300 """ 301 Retrieves the identifiers of all of the HSMs provisioned for 302 the current customer. 303 304 This operation supports pagination with the use of the 305 NextToken member. If more results are available, the NextToken 306 member of the response contains a token that you pass in the 307 next call to ListHsms to retrieve the next set of items. 308 309 :type next_token: string 310 :param next_token: The NextToken value from a previous call to 311 ListHsms. Pass null if this is the first call. 312 313 """ 314 params = {} 315 if next_token is not None: 316 params['NextToken'] = next_token 317 return self.make_request(action='ListHsms', 318 body=json.dumps(params)) 319 320 def list_luna_clients(self, next_token=None): 321 """ 322 Lists all of the clients. 323 324 This operation supports pagination with the use of the 325 NextToken member. If more results are available, the NextToken 326 member of the response contains a token that you pass in the 327 next call to ListLunaClients to retrieve the next set of 328 items. 329 330 :type next_token: string 331 :param next_token: The NextToken value from a previous call to 332 ListLunaClients. Pass null if this is the first call. 333 334 """ 335 params = {} 336 if next_token is not None: 337 params['NextToken'] = next_token 338 return self.make_request(action='ListLunaClients', 339 body=json.dumps(params)) 340 341 def modify_hapg(self, hapg_arn, label=None, partition_serial_list=None): 342 """ 343 Modifies an existing high-availability partition group. 344 345 :type hapg_arn: string 346 :param hapg_arn: The ARN of the high-availability partition group to 347 modify. 348 349 :type label: string 350 :param label: The new label for the high-availability partition group. 351 352 :type partition_serial_list: list 353 :param partition_serial_list: The list of partition serial numbers to 354 make members of the high-availability partition group. 355 356 """ 357 params = {'HapgArn': hapg_arn, } 358 if label is not None: 359 params['Label'] = label 360 if partition_serial_list is not None: 361 params['PartitionSerialList'] = partition_serial_list 362 return self.make_request(action='ModifyHapg', 363 body=json.dumps(params)) 364 365 def modify_hsm(self, hsm_arn, subnet_id=None, eni_ip=None, 366 iam_role_arn=None, external_id=None, syslog_ip=None): 367 """ 368 Modifies an HSM. 369 370 :type hsm_arn: string 371 :param hsm_arn: The ARN of the HSM to modify. 372 373 :type subnet_id: string 374 :param subnet_id: The new identifier of the subnet that the HSM is in. 375 376 :type eni_ip: string 377 :param eni_ip: The new IP address for the elastic network interface 378 attached to the HSM. 379 380 :type iam_role_arn: string 381 :param iam_role_arn: The new IAM role ARN. 382 383 :type external_id: string 384 :param external_id: The new external ID. 385 386 :type syslog_ip: string 387 :param syslog_ip: The new IP address for the syslog monitoring server. 388 389 """ 390 params = {'HsmArn': hsm_arn, } 391 if subnet_id is not None: 392 params['SubnetId'] = subnet_id 393 if eni_ip is not None: 394 params['EniIp'] = eni_ip 395 if iam_role_arn is not None: 396 params['IamRoleArn'] = iam_role_arn 397 if external_id is not None: 398 params['ExternalId'] = external_id 399 if syslog_ip is not None: 400 params['SyslogIp'] = syslog_ip 401 return self.make_request(action='ModifyHsm', 402 body=json.dumps(params)) 403 404 def modify_luna_client(self, client_arn, certificate): 405 """ 406 Modifies the certificate used by the client. 407 408 This action can potentially start a workflow to install the 409 new certificate on the client's HSMs. 410 411 :type client_arn: string 412 :param client_arn: The ARN of the client. 413 414 :type certificate: string 415 :param certificate: The new certificate for the client. 416 417 """ 418 params = { 419 'ClientArn': client_arn, 420 'Certificate': certificate, 421 } 422 return self.make_request(action='ModifyLunaClient', 423 body=json.dumps(params)) 424 425 def make_request(self, action, body): 426 headers = { 427 'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action), 428 'Host': self.region.endpoint, 429 'Content-Type': 'application/x-amz-json-1.1', 430 'Content-Length': str(len(body)), 431 } 432 http_request = self.build_base_http_request( 433 method='POST', path='/', auth_path='/', params={}, 434 headers=headers, data=body) 435 response = self._mexe(http_request, sender=None, 436 override_num_retries=10) 437 response_body = response.read().decode('utf-8') 438 boto.log.debug(response_body) 439 if response.status == 200: 440 if response_body: 441 return json.loads(response_body) 442 else: 443 json_body = json.loads(response_body) 444 fault_name = json_body.get('__type', None) 445 exception_class = self._faults.get(fault_name, self.ResponseError) 446 raise exception_class(response.status, response.reason, 447 body=json_body) 448 449