def pair_device(auth_code, user_auth_credentials, device_encryption_info, encryption_context, server_name="", server_app_id = "", config=SplunkConfig()): """ Part 2/2 of the registration process. Send splunk app's public key information and encrypted credentials for user to cloudgateway. Upon success, this will complete the credential swap as now the client has the splunk app's credentials and the splunk app has received the app's public key :param auth_code: auth code of the device :param user_auth_credentials: UserAuthCredentials object interface which captures different forms of session tokens :param device_encryption_info: DeviceInfo object which was returned in the authenticate_code api call :param encryption_context: EncryptionContext object. Can be a regular EncryptionContext or a subclass such as SplunkEncryptionContext depending on whether you want to run in standalone mode or not. :param server_name: optional parameter for name of server so that device can identify which instance it is paired with :param config: CloudgatewaySdkConfig object :return: """ user_auth_credentials.validate() sodium_client = encryption_context.sodium_client encrypt_public_key = encryption_context.encrypt_public_key() sign_public_key = encryption_context.sign_public_key() encryption_func = partial(encrypt_for_send, sodium_client, device_encryption_info.encrypt_public_key) auth_header = sb_auth_header(encryption_context) session_token = user_auth_credentials.get_credentials() if sys.version_info < (3, 0) else str.encode(user_auth_credentials.get_credentials()) encrypted_session_token = encryption_context.secure_session_token(session_token) encrypted_credentials_bundle = build_encypted_credentials_bundle(user_auth_credentials.get_username(), encrypted_session_token, encryption_func, server_app_id, deployment_name=server_name) pair_device_with_sb(auth_code, auth_header, device_encryption_info.device_id, encrypt_public_key, sign_public_key, encrypted_credentials_bundle, config)
def send_public_key_to_spacebridge(websocket_protocol, config=SplunkConfig()): """ Send the splapp public signing key to spacebridge Abstraction layer for the spacebrige request. This function: 1. Signs the splapp public signing key with the splapp private signing key 2. Creates a proto request with the public signing key and the signature 3. Sends the proto request to spacebridge :param websocket_protocol: The websocket protocol :param config: The configuration used to make the request """ websocket_protocol.logger.debug('Starting sending splapp public key to spacebridge') encryption_context = websocket_protocol.encryption_context sign_func = partial(sign_detached, encryption_context.sodium_client, encryption_context.sign_private_key() ) public_signing_key = encryption_context.sign_public_key() request_proto = http_pb2.RegisterSigningPublicKeyRequest() request_proto.publicKeyForSigning = public_signing_key request_proto.signature = sign_func(public_signing_key) serialized_proto = request_proto.SerializeToString() headers = {'Content-Type': 'application/x-protobuf', 'Authorization': str(sb_auth_header(encryption_context))} client = AsyncClient() yield send_key(headers, serialized_proto, config, client, websocket_protocol) websocket_protocol.logger.debug('Finished send_key method. No logs after start message indicate success.')
def make_device_authentication_request(device_info, encryption_context, config): """Makes a device authentication request to Cloud Gateway. If successful, Cloud Gateway will return a DeviceAuthenticationResponse object. Args: device_info ([DeviceInfo]): device info object containing client's public keys encryption_context ([EncryptionContext]): Raises: RestException.CloudgatewayServerError Returns: [Requests.Response]: response object whose content is a serialized DeviceAuthenticationResponse proto """ request_proto = build_device_authentication_request(device_info) try: spacebridge_header = {'Authorization': sb_auth_header(encryption_context)} return requests.post(sb_client_auth_endpoint(config), headers=spacebridge_header, data=request_proto.SerializeToString(), ) except Exception as e: raise RestException.CloudgatewayServerError('Unable to reach cloudgateway: {0}'.format(e), 503)
def send(self, device_id, payload, request_id=None): """Send a message to a particular device using Cloud Gateway Args: device_id ([binary]): id of the device to send the message to payload ([string]): Message to be sent. Can be any format you want (json, serialized proto, etc.) request_id (str, optional): [description]. Defaults to "123". Returns: [requests.response]: response returned by requests object """ if not request_id: request_id = ServerResponse.create_rid() send_message_request = http_pb2.SendMessageRequest() recipient_info = self.message_handler.fetch_device_info(device_id) build_encrypted_payload( recipient_info, self.encryption_context, payload, request_id, self.logger, signed_envelope=send_message_request.signedEnvelope) # self.connector.factory.protocol.sendMessage(send_message_request.SerializeToString()) spacebridge_header = { 'Authorization': sb_auth_header(self.encryption_context) } with requests_ssl_context(self.key_bundle) as cert: return requests.post(sb_message_endpoint(self.config), headers=spacebridge_header, data=send_message_request.SerializeToString(), cert=cert.name)
def make_authentication_result_request(auth_code, encryption_context, config, key_bundle=None): """ Make AuthenticationResultRequest to Cloud Gateway Args: auth_code ([string]): 10-digit auth code returned by Cloud Gateway on Device Authentication Request encryption_context ([EncryptionContext]): Raises: RestException.CloudgatewayServerError: Returns: [Requests.Response]: Response object containing serialized AuthenticationResultResponse object """ request_proto = build_authentication_result_request(auth_code) with requests_ssl_context(key_bundle) as cert: try: spacebridge_header = { 'Authorization': sb_auth_header(encryption_context) } return requests.get(sb_client_auth_result_endpoint( auth_code, config), headers=spacebridge_header, data=request_proto.SerializeToString(), cert=cert.name) except Exception as e: raise RestException.CloudgatewayServerError( 'Unable to reach cloudgateway: {0}'.format(e), 503)
def async_send_confirmation_result(confirmation_result, encryption_context, async_sb_client): """ Send an async http request to spacebridge to complete MDM registration Args: confirmation_result (MdmAuthenticationConfirmationResult) encryption_context (EncryptionContext) async_sb_client (AsyncSpacebridgeClient): """ return async_sb_client.async_send_request( api='/api/mdm/confirm', auth_header=None, data=confirmation_result.SerializeToString(), headers={'Content-Type': 'application/x-protobuf', 'Authorization': sb_auth_header(encryption_context)} )
def submit_auth_code(auth_code, encryption_context, config): """ Given an auth code, submit it to cloudgateway's auth endpoint. Raise an exception if cannot reach cloudgateway :param auth_code :param encryption_context :return: seriealized protobuf response from cloudgateway """ try: spacebridge_header = { 'Authorization': sb_auth_header(encryption_context) } return requests.get(sb_auth_endpoint(auth_code, config), headers=spacebridge_header, proxies=config.get_proxies()) except Exception as e: raise RestException.CloudgatewayServerError( 'Unable to reach cloudgateway: {0}'.format(e), 503)
def unregister_device(device_id, encryption_context, config=SplunkConfig()): """ Unregister ascljk a device from cloud gateway. Initiating this will cause cloud gateway to remove the routing entry and also force the client device to unregister :param device_id: device id from DeviceInfo object :param encryption_context: EncryptionContext object. Can be a regular EncryptionContext or a subclass such as SplunkEncryptionContext depending on whether you want to run in standalone mode or not. :return: DeviceUnregistrationResponse proto or throws an exception if the request can't be completed :param config: CloudgatewaySdkConfig object """ unregister_proto = unregister.build_device_unregister_req(device_id, encryption_context) raw_response = unregister.make_unregister_req(unregister_proto, sb_auth_header(encryption_context), config) unregister.parse_sb_response(raw_response) return raw_response
def build_client_factory(self): """ Setup a cloudgatewayclientfactory object before a connection is established to Cloudgateway. Configures things like the uri to connect on, auth headers, websocket protocol options, etc. Returns: CloudgatewayClientFactory object """ headers = {'Authorization': sb_auth_header(self.encryption_context)} if self.shard_id: headers[constants.HEADER_SHARD_ID] = self.shard_id self.logger.info("Using shard_id={}".format(self.shard_id)) ws_url = "wss://{0}/deployment".format( self.config.get_spacebridge_server()) proxy, auth = self.config.get_ws_https_proxy_settings() if auth: headers['Proxy-Authorization'] = 'Basic ' + auth if sys.version_info < (3, 0): factory = cloudgateway_client_protocol.CloudgatewayClientFactory( ws_url, headers=headers, proxy=proxy) factory.configure( cloudgateway_client_protocol.SpacebridgeWebsocketProtocol, self.max_reconnect_delay) factory.setProtocolOptions(autoFragmentSize=65536) else: factory = WebSocketClientFactory(ws_url, headers=headers) factory.protocol = aio_client_protocol.AioSpacebridgeServerProtocol factory.autoFragmentSize = 65536 factory.protocol.encryption_context = self.encryption_context factory.protocol.system_auth_header = SplunkAuthHeader( self.system_session_key) factory.protocol.parent_process_monitor = self.parent_process_monitor factory.protocol.logger = self.logger factory.protocol.mode = self.mode factory.protocol.cluster_monitor = self.cluster_monitor factory.protocol.websocket_context = self.websocket_context return factory
def submit_auth_code(auth_code, encryption_context, config, key_bundle=None): """ Given an auth code, submit it to cloudgateway's auth endpoint. Raise an exception if cannot reach cloudgateway :param auth_code :param encryption_context :param mtls_pkcs12: A PKCS12 object containing the certificate and private key information for mTLS :return: seriealized protobuf response from cloudgateway """ with requests_ssl_context(key_bundle) as cert: try: spacebridge_header = {'Authorization': sb_auth_header(encryption_context)} return requests.get(sb_auth_endpoint(auth_code, config), headers=spacebridge_header, proxies=config.get_proxies(), cert=cert.name ) except Exception as e: raise RestException.CloudgatewayServerError('Unable to reach cloudgateway: {0}'.format(e), 503)
def make_mdm_authentication_request(username, password, server_info, encryption_context, mdm_sign_private_key, config): """ Make AuthenticationResultRequest to Cloud Gateway Args: auth_code ([string]): 10-digit auth code returned by Cloud Gateway on Device Authentication Request encryption_context ([EncryptionContext]): Raises: RestException.CloudgatewayServerError: Returns: [Requests.Response]: Response object containing serialized AuthenticationResultResponse object """ request_proto = build_mdm_authentication_request(username, password, encryption_context, server_info, mdm_sign_private_key) try: return requests.post( url='{0}/api/mdm/authenticate'.format(config.get_spacebridge_domain()), headers={'Content-Type': 'application/x-protobuf', 'Authorization': sb_auth_header(encryption_context)}, data=request_proto.SerializeToString(), proxies=config.get_proxies() ) except Exception as e: raise RestException.CloudgatewayServerError('Unable to reach cloudgateway: {0}'.format(e), 503)