def doRegisterAgent(registrar_ip,registrar_port,agent_id,tpm_version,pub_ek,ekcert,pub_aik,pub_ek_tpm=None,aik_name=None): data = { 'ek': pub_ek, 'ekcert': ekcert, 'aik': pub_aik, 'aik_name': aik_name, 'ek_tpm': pub_ek_tpm, 'tpm_version': tpm_version, } v_json_message = json.dumps(data) params = '/agents/%s'% (agent_id) response = httpclient_requests.request("POST", "%s"%(registrar_ip), registrar_port, params=params, data=v_json_message, context=None) response_body = json.loads(response.read().decode("utf-8")) if response.status != 200: logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status)) keylime_logging.log_http_response(logger,logging.ERROR,response_body) return None logger.info("Agent registration requested for %s"%agent_id) if "results" not in response_body: logger.critical("Error: unexpected http response body from Registrar Server: %s"%str(response.status)) return None if "blob" not in response_body["results"]: logger.critical("Error: did not receive blob from Registrar Server: %s"%str(response.status)) return None return response_body["results"]["blob"]
def do_cvdelete(self): params = f'/agents/{self.agent_uuid}' response = httpclient_requests.request("DELETE", "%s"%(self.cloudverifier_ip), self.cloudverifier_port, params=params, context=self.context) if response == 503: logger.error(f"Cannot connect to Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port}. Connection refused.") exit() elif response == 504: logger.error(f"Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port} timed out.") exit() if response.status == 202: deleted = False for _ in range(12): response = httpclient_requests.request("GET", "%s"%(self.cloudverifier_ip), self.cloudverifier_port, params=params, context=self.context) if response.status == 404: deleted=True break time.sleep(.4) if deleted: logger.info(f"CV completed deletion of agent {self.agent_uuid}") else: logger.error(f"Timed out waiting for delete of agent {self.agent_uuid} to complete at CV") exit() elif response.status == 200: logger.info(f"Agent {self.agent_uuid} deleted from the CV") else: response_body = json.loads(response.read().decode()) keylime_logging.log_http_response(logger,logging.ERROR,response_body)
def do_cvreactivate(self): """ Reactive Agent """ do_cvreactivate = RequestsClient( self.verifier_base_url, self.tls_enabled) response = do_cvreactivate.put( (f'/agents/{self.agent_uuid}/reactivate'), data=b'', cert=self.cert, verify=False ) if response.status_code == 503: logger.error("Cannot connect to Verifier at %s with Port %s. Connection refused.", self.verifier_ip, self.verifier_port) sys.exit() elif response.status_code == 504: logger.error("Verifier at %s with Port %s timed out.", self.verifier_ip, self.verifier_port) sys.exit() response_body = response.json() if response.status_code != 200: keylime_logging.log_http_response( logger, logging.ERROR, response_body) logger.error("Update command response: %s Unexpected response from Cloud Verifier.", response.status_code) else: logger.info("Agent %s re-activated", self.agent_uuid)
def getKeys(registrar_ip,registrar_port,agent_id): global context #make absolutely sure you don't ask for AIKs unauthenticated if context is None or context.verify_mode != ssl.CERT_REQUIRED: raise Exception("It is unsafe to use this interface to query AIKs with out server authenticated TLS") try: params = '/agents/%s'% (agent_id) response = httpclient_requests.request("GET", "%s"%(registrar_ip), registrar_port, params=params, context=context) response_body = json.loads(response.read().decode()) if response.status != 200: logger.critical("Error: unexpected http response code from Registrar Server: %s"%str(response.status)) keylime_logging.log_http_response(logger,logging.CRITICAL,response_body) return None if "results" not in response_body: logger.critical("Error: unexpected http response body from Registrar Server: %s"%str(response.status)) return None if "aik" not in response_body["results"]: logger.critical("Error: did not receive aik from Registrar Server: %s"%str(response.status)) return None return response_body["results"] except Exception as e: logger.exception(e) return None
def doRegisterAgent(registrar_ip, registrar_port, agent_id, tpm_version, pub_ek, ekcert, pub_aik, pub_ek_tpm=None, aik_name=None): data = { 'ek': pub_ek, 'ekcert': ekcert, 'aik': pub_aik, 'aik_name': aik_name, 'ek_tpm': pub_ek_tpm, 'tpm_version': tpm_version, } response = None try: client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.post(f'/agents/{agent_id}', cert=tls_cert_info, data=json.dumps(data), verify=False) response_body = response.json() if response.status_code != 200: logger.error( f"Error: unexpected http response code from Registrar Server: {response.status_code}" ) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return None logger.info(f"Agent registration requested for {agent_id}") if "results" not in response_body: logger.critical( f"Error: unexpected http response body from Registrar Server: {response.status_code}" ) return None if "blob" not in response_body["results"]: logger.critical( f"Error: did not receive blob from Registrar Server: {response.status_code}" ) return None return response_body["results"]["blob"] except Exception as e: if response and response.status_code == 503: logger.error( f"Agent cannot establish connection to registrar at {registrar_ip}:{registrar_port}" ) sys.exit() else: logger.exception(e) return None
def do_cvstop(self): """ Stop declared active agent """ params = f'/agents/{self.agent_uuid}/stop' do_cvstop = RequestsClient(self.verifier_base_url, self.tls_enabled) response = do_cvstop.put( params, cert=self.cert, data=b'', verify=False ) if response.status_code == 503: logger.error("Cannot connect to Verifier at %s with Port %s. Connection refused.", self.verifier_ip, self.verifier_port) sys.exit() elif response.status_code == 504: logger.error("Verifier at %s with Port %s timed out.", self.verifier_ip, self.verifier_port) sys.exit() response_body = response.json() if response.status_code != 200: keylime_logging.log_http_response( logger, logging.ERROR, response_body) else: logger.info("Agent %s stopped", self.agent_uuid)
async def get_agent_state(self, agent_id): try: get_agent_state = RequestsClient(verifier_base_url, tls_enabled) response = get_agent_state.get( (f'/agents/{agent_id}'), cert=cert, verify=False ) except Exception as e: logger.error("Status command response: %s:%s Unexpected response from Cloud Verifier.", tenant_templ.cloudverifier_ip, tenant_templ.cloudverifier_port) logger.exception(e) config.echo_json_response( self, 500, "Unexpected response from Cloud Verifier", str(e)) logger.error("Unexpected response from Cloud Verifier: %s", e) return inst_response_body = response.json() if response.status_code != 200 and response.status_code != 404: logger.error("Status command response: %d Unexpected response from Cloud Verifier.", response.status_code) keylime_logging.log_http_response( logger, logging.ERROR, inst_response_body) return None if "results" not in inst_response_body: logger.critical("Error: unexpected http response body from Cloud Verifier: %s", response.status_code) return None # Agent not added to CV (but still registered) if response.status_code == 404: return {"operational_state": states.REGISTERED} return inst_response_body["results"]
def do_cvstop(self): # params = '/agents/%s/stop'% (self.agent_uuid) params = f'/agents/{self.agent_uuid}/stop' response = httpclient_requests.request("PUT", "%s" % (self.cloudverifier_ip), self.cloudverifier_port, params=params, data=b'', context=self.context) if response == 503: logger.error( f"Cannot connect to Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port}. Connection refused." ) exit() elif response == 504: logger.error( f"Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port} timed out." ) exit() response_body = json.loads(response.read().decode()) if response.status != 200: keylime_logging.log_http_response(logger, logging.ERROR, response_body) else: logger.info(f"Agent {self.agent_uuid} stopped")
def do_cvreactivate(self): #params = '/agents/%s/reactivate'% (self.agent_uuid) params = f'/agents/{self.agent_uuid}/reactivate' response = httpclient_requests.request("PUT", "%s" % (self.cloudverifier_ip), self.cloudverifier_port, params=params, data=b'', context=self.context) if response == 503: logger.error( f"Cannot connect to Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port}. Connection refused." ) exit() elif response == 504: logger.error( f"Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port} timed out." ) exit() response_body = json.loads(response.read().decode()) if response.status != 200: keylime_logging.log_http_response(logger, logging.ERROR, response_body) raise UserError( "Update command response: %d Unexpected response from Cloud Verifier." % response.status) else: logger.info(f"Agent {self.agent_uuid} re-activated")
def do_verify(self): """ Perform verify using a random generated challenge """ challenge = TPM_Utilities.random_password(20) numtries = 0 while True: try: cloudagent_base_url = (f'{self.agent_ip}:{self.agent_port}') do_verify = RequestsClient(cloudagent_base_url, tls_enabled=False) response = do_verify.get( (f'/keys/verify?challenge={challenge}'), cert=self.cert, verify=False) except Exception as e: if response.status_code in (503, 504): numtries += 1 maxr = config.getint('tenant', 'max_retries') if numtries >= maxr: logger.error( f"Cannot establish connection to agent on {self.agent_ip} with port {self.agent_port}" ) sys.exit() retry = config.getfloat('tenant', 'retry_interval') logger.info( f"Verifier connection to agent at {self.agent_ip} refused {numtries}/{maxr} times, trying again in {retry} seconds..." ) time.sleep(retry) continue raise e response_body = response.json() if response.status_code == 200: if "results" not in response_body or 'hmac' not in response_body[ 'results']: logger.critical( f"Error: unexpected http response body from Cloud Agent: {response.status_code}" ) break mac = response_body['results']['hmac'] ex_mac = crypto.do_hmac(self.K, challenge) if mac == ex_mac: logger.info("Key derivation successful") else: logger.error("Key derivation failed") else: keylime_logging.log_http_response(logger, logging.ERROR, response_body) retry = config.getfloat('tenant', 'retry_interval') logger.warning( f"Key derivation not yet complete...trying again in {retry} seconds...Ctrl-C to stop" ) time.sleep(retry) continue break
def doRegistrarDelete(registrar_ip,registrar_port, agent_id): global context params = '/agents/%s'% (agent_id) response = httpclient_requests.request("DELETE", "%s"%(registrar_ip), registrar_port, params=params, context=None) response_body = json.loads(response) if response.status == 200: logger.debug("Registrar deleted.") else: logger.warn("Status command response: " + str(response.status) + " Unexpected response from registrar.") keylime_logging.log_http_response(logger,logging.WARNING,response_body)
def doRegistrarDelete(registrar_ip, registrar_port, agent_id): client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.delete(f'/v{api_version}/agents/{agent_id}', cert=tls_cert_info, verify=False) response_body = response.json() if response.status_code == 200: logger.debug("Registrar deleted.") else: logger.warning("Status command response: %s Unexpected response from registrar.", response.status_code) keylime_logging.log_http_response(logger, logging.WARNING, response_body)
def do_cv(self): """initiaite v, agent_id and ip initiate the cloudinit sequence""" b64_v = base64.b64encode(self.V).decode('utf-8') logger.debug("b64_v:" + b64_v) data = { 'v': b64_v, 'cloudagent_ip': self.cv_cloudagent_ip, 'cloudagent_port': self.cloudagent_port, 'providerverifier_ip': self.providerverifier_ip, 'providerverifier_port': self.providerverifier_port, 'need_provider_quote': self.need_provider_quote, 'tpm_policy': json.dumps(self.tpm_policy), 'vtpm_policy': json.dumps(self.vtpm_policy), 'ima_whitelist': json.dumps(self.ima_whitelist), 'metadata': json.dumps(self.metadata), 'revocation_key': self.revocation_key, 'accept_tpm_hash_algs': self.accept_tpm_hash_algs, 'accept_tpm_encryption_algs': self.accept_tpm_encryption_algs, 'accept_tpm_signing_algs': self.accept_tpm_signing_algs, } json_message = json.dumps(data) params = f'/agents/{self.agent_uuid}' #params = '/agents/%s'% (self.agent_uuid) response = httpclient_requests.request("POST", "%s" % (self.cloudverifier_ip), self.cloudverifier_port, params=params, data=json_message, context=self.context) if response == 503: logger.error( f"Cannot connect to Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port}. Connection refused." ) exit() elif response == 504: logger.error( f"Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port} timed out." ) exit() if response.status == 409: # this is a conflict, need to update or delete it logger.error( "Agent %s already existed at CV. Please use delete or update." % self.agent_uuid) exit() elif response.status != 200: keylime_logging.log_http_response(logger, logging.ERROR, response.read().decode()()) logger.error( f"POST command response: {response.status} Unexpected response from Cloud Verifier: {response.read().decode()}" ) exit()
def doRegistrarList(registrar_ip, registrar_port): client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.get(f'/v{api_version}/agents/', cert=tls_cert_info, verify=False) response_body = response.json() if response.status_code != 200: logger.warning("Registrar returned: %s Unexpected response from registrar.", response.status_code) keylime_logging.log_http_response(logger, logging.WARNING, response_body) return None return response_body
def getData(registrar_ip, registrar_port, agent_id): # make absolutely sure you don't ask for data that contains AIK keys unauthenticated if not tls_enabled: raise Exception( "It is unsafe to use this interface to query AIKs without server-authenticated TLS.") response = None try: client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.get(f'/v{api_version}/agents/{agent_id}', cert=tls_cert_info, verify=False) response_body = response.json() if response.status_code != 200: logger.critical("Error: unexpected http response code from Registrar Server: %s", response.status_code) keylime_logging.log_http_response(logger, logging.CRITICAL, response_body) return None # Check for all values that are consumed by other parts of Keylime if "results" not in response_body: logger.critical("Error: unexpected http response body from Registrar Server: %s", response.status_code) return None if "aik_tpm" not in response_body["results"]: logger.critical("Error: did not receive AIK from Registrar Server.") return None if "regcount" not in response_body["results"]: logger.critical("Error: did not receive regcount from Registrar Server.") return None if "ek_tpm" not in response_body["results"]: logger.critical("Error: did not receive EK from Registrar Server.") return None if "ip" not in response_body["results"]: logger.critical("Error: did not receive IP from Registrar Server.") return None if "port" not in response_body["results"]: logger.critical("Error: did not receive port from Registrar Server.") return None return response_body["results"] except AttributeError as e: if response and response.status_code == 503: logger.critical("Error: the registrar is not available at %s:%s", registrar_ip, registrar_port) else: logger.exception(e) except Exception as e: logger.exception(e) return None
def do_verify(self): challenge = TPM_Utilities.random_password(20) numtries = 0 while True: try: params = f'/keys/verify?challenge={challenge}' response = httpclient_requests.request("GET", "%s" % (self.cloudagent_ip), self.cloudagent_port, params=params) except Exception as e: if response == 503 or 504: numtries += 1 maxr = config.getint('tenant', 'max_retries') if numtries >= maxr: logger.error( f"Cannot establish connection to agent on {self.cloudagent_ip} with port {self.cloudagent_port}" ) exit() retry = config.getfloat('tenant', 'retry_interval') logger.info( f"Verifier connection to agent at {self.cloudagent_ip} refused {numtries}/{maxr} times, trying again in {retry} seconds..." ) time.sleep(retry) continue else: raise (e) response_body = json.loads(response.read().decode()) if response.status == 200: if "results" not in response_body or 'hmac' not in response_body[ 'results']: logger.critical( f"Error: unexpected http response body from Cloud Agent: {response.status}" ) break mac = response_body['results']['hmac'] ex_mac = crypto.do_hmac(self.K, challenge) if mac == ex_mac: logger.info("Key derivation successful") else: logger.error("Key derivation failed") else: keylime_logging.log_http_response(logger, logging.ERROR, response_body) retry = config.getfloat('tenant', 'retry_interval') logger.warning( f"Key derivation not yet complete...trying again in {retry} seconds...Ctrl-C to stop" ) time.sleep(retry) continue break
def doRegisterAgent(registrar_ip, registrar_port, agent_id, ek_tpm, ekcert, aik_tpm): data = { 'ekcert': ekcert, 'aik_tpm': aik_tpm, } if ekcert is None or ekcert == 'emulator': data['ek_tpm'] = ek_tpm response = None try: client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.post(f'/agents/{agent_id}', cert=tls_cert_info, data=json.dumps(data), verify=False) response_body = response.json() if response.status_code != 200: logger.error( "Error: unexpected http response code from Registrar Server: %s", response.status_code) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return None logger.info("Agent registration requested for %s", agent_id) if "results" not in response_body: logger.critical( "Error: unexpected http response body from Registrar Server: %s", response.status_code) return None if "blob" not in response_body["results"]: logger.critical( "Error: did not receive blob from Registrar Server: %s", response.status_code) return None return response_body["results"]["blob"] except Exception as e: if response and response.status_code == 503: logger.error( "Agent cannot establish connection to registrar at %s:%s", registrar_ip, registrar_port) sys.exit() else: logger.exception(e) return None
def getKeys(registrar_ip, registrar_port, agent_id): # make absolutely sure you don't ask for AIKs unauthenticated if not tls_enabled: raise Exception( "It is unsafe to use this interface to query AIKs without server-authenticated TLS." ) response = None try: client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.get(f'/agents/{agent_id}', cert=tls_cert_info, verify=False) response_body = response.json() if response.status_code != 200: logger.critical( "Error: unexpected http response code from Registrar Server: %s" % str(response.status_code)) keylime_logging.log_http_response(logger, logging.CRITICAL, response_body) return None if "results" not in response_body: logger.critical( "Error: unexpected http response body from Registrar Server: %s" % str(response.status_code)) return None if "aik" not in response_body["results"]: logger.critical( "Error: did not receive AIK from Registrar Server: %s" % str(response.status_code)) return None return response_body["results"] except AttributeError as e: if response and response.status_code == 503: logger.critical("Error: the registrar is not available at %s:%s" % (registrar_ip, registrar_port)) else: logger.exception(e) except Exception as e: logger.exception(e) return None
def do_cv(self): """ Initiaite v, agent_id and ip and initiate the cloudinit sequence """ b64_v = base64.b64encode(self.V).decode('utf-8') logger.debug("b64_v:" + b64_v) data = { 'v': b64_v, 'cloudagent_ip': self.cv_cloudagent_ip, 'cloudagent_port': self.agent_port, 'tpm_policy': json.dumps(self.tpm_policy), 'vtpm_policy': json.dumps(self.vtpm_policy), 'allowlist': json.dumps(self.allowlist), 'metadata': json.dumps(self.metadata), 'revocation_key': self.revocation_key, 'accept_tpm_hash_algs': self.accept_tpm_hash_algs, 'accept_tpm_encryption_algs': self.accept_tpm_encryption_algs, 'accept_tpm_signing_algs': self.accept_tpm_signing_algs, } json_message = json.dumps(data) do_cv = RequestsClient(self.verifier_base_url, self.tls_enabled) response = do_cv.post((f'/agents/{self.agent_uuid}'), data=json_message, cert=self.cert, verify=False) if response.status_code == 503: logger.error( f"Cannot connect to Verifier at {self.verifier_ip} with Port {self.verifier_port}. Connection refused." ) sys.exit() elif response.status_code == 504: logger.error( f"Verifier at {self.verifier_ip} with Port {self.verifier_port} timed out." ) sys.exit() if response.status_code == 409: # this is a conflict, need to update or delete it logger.error( f"Agent {self.agent_uuid} already existed at CV. Please use delete or update." ) sys.exit() elif response.status_code != 200: keylime_logging.log_http_response(logger, logging.ERROR, response.json()) logger.error( f"POST command response: {response.status} Unexpected response from Cloud Verifier: {response.read()}" ) sys.exit()
def doActivateAgent(registrar_ip,registrar_port,agent_id,key): data = { 'auth_tag': crypto.do_hmac(key,agent_id), } v_json_message = json.dumps(data) params = '/agents/%s/activate'% (agent_id) response = httpclient_requests.request("PUT", "%s"%(registrar_ip), registrar_port, params=params, data=v_json_message, context=None) response_body = json.loads(response.read().decode()) if response.status == 200: logger.info("Registration activated for agent %s."%agent_id) return True else: logger.error("Error: unexpected http response code from Registrar Server: " + str(response.status)) keylime_logging.log_http_response(logger,logging.ERROR,response_body) return False
def doActivateVirtualAgent(registrar_ip, registrar_port, agent_id, deepquote): data = {'deepquote': deepquote} client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.put(f'/agents/{agent_id}/vactivate', cert=tls_cert_info, data=json.dumps(data), verify=False) response_body = response.json() if response.status_code == 200: logger.info("Registration activated for agent %s." % agent_id) return True logger.error( "Error: unexpected http response code from Registrar Server: " + str(response.status_code)) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return False
def doActivateAgent(registrar_ip, registrar_port, agent_id, key): data = { 'auth_tag': crypto.do_hmac(key, agent_id), } client = RequestsClient(f'{registrar_ip}:{registrar_port}', tls_enabled) response = client.put(f'/v{api_version}/agents/{agent_id}/activate', cert=tls_cert_info, data=json.dumps(data), verify=False) response_body = response.json() if response.status_code == 200: logger.info("Registration activated for agent %s.", agent_id) return True logger.error( "Error: unexpected http response code from Registrar Server: %s", str(response.status_code)) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return False
def do_cvdelete(self): """Delete agent from Verifier """ do_cvdelete = RequestsClient(self.verifier_base_url, self.tls_enabled) response = do_cvdelete.delete( (f'/agents/{self.agent_uuid}'), cert=self.cert, verify=False ) if response.status_code == 503: logger.error( f"Cannot connect to Verifier at {self.verifier_ip} with Port {self.verifier_port}. Connection refused.") sys.exit() elif response.status_code == 504: logger.error( f"Verifier at {self.verifier_ip} with Port {self.verifier_port} timed out.") sys.exit() if response.status_code == 202: deleted = False for _ in range(12): get_cvdelete = RequestsClient( self.verifier_base_url, self.tls_enabled) response = get_cvdelete.get( (f'/agents/{self.agent_uuid}'), cert=self.cert, verify=False ) if response.status_code in (200, 404): deleted = True break time.sleep(.4) if deleted: logger.info( f"CV completed deletion of agent {self.agent_uuid}") else: logger.error( f"Timed out waiting for delete of agent {self.agent_uuid} to complete at CV") sys.exit() elif response.status_code == 200: logger.info(f"Agent {self.agent_uuid} deleted from the CV") else: response_body = response.json() keylime_logging.log_http_response( logger, logging.ERROR, response_body)
async def get_agent_state(self, agent_id): try: res = tornado_requests.request( "GET", "http://%s:%s/agents/%s" % (tenant_templ.cloudverifier_ip, tenant_templ.cloudverifier_port, agent_id), context=tenant_templ.context) response = await res except Exception as e: logger.error( "Status command response: %s:%s Unexpected response from Cloud Verifier." % (tenant_templ.cloudverifier_ip, tenant_templ.cloudverifier_port)) logger.exception(e) common.echo_json_response( self, 500, "Unexpected response from Cloud Verifier", str(e)) logger.error("Unexpected response from Cloud Verifier: ", str(e)) return inst_response_body = response.json() if response.status_code != 200 and response.status_code != 404: logger.error( "Status command response: %d Unexpected response from Cloud Verifier." % response.status_code) keylime_logging.log_http_response(logger, logging.ERROR, inst_response_body) return None if "results" not in inst_response_body: logger.critical( "Error: unexpected http response body from Cloud Verifier: %s" % str(response.status_code)) return None # Agent not added to CV (but still registered) if response.status_code == 404: return { "operational_state": cloud_verifier_common.CloudAgent_Operational_State.REGISTERED } else: return inst_response_body["results"] return None
async def get(self): """This method handles the GET requests to retrieve status on agents from the WebApp. Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's will return errors. """ rest_params = config.get_restful_params(self.request.uri) if rest_params is None: config.echo_json_response( self, 405, "Not Implemented: Use /agents/ or /logs/ interface") return if "logs" in rest_params and rest_params["logs"] == "tenant": offset = 0 if "pos" in rest_params and rest_params[ "pos"] is not None and rest_params["pos"].isdigit(): offset = int(rest_params["pos"]) # intercept requests for logs with open(keylime_logging.LOGSTREAM, 'r') as f: logValue = f.readlines() config.echo_json_response(self, 200, "Success", {'log': logValue[offset:]}) return if "agents" not in rest_params: # otherwise they must be looking for agent info config.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: %s', self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # Handle request for specific agent data separately agents = await self.get_agent_state(agent_id) agents["id"] = agent_id config.echo_json_response(self, 200, "Success", agents) return # If no agent ID, get list of all agents from Registrar try: get_agents = RequestsClient(registrar_base_tls_url, tls_enabled) response = get_agents.get(('/agents/'), cert=cert, verify=False) except Exception as e: logger.error( "Status command response: %s:%s Unexpected response from Registrar.", tenant_templ.registrar_ip, tenant_templ.registrar_port) logger.exception(e) config.echo_json_response(self, 500, "Unexpected response from Registrar", str(e)) return response_body = response.json() if response.status_code != 200: logger.error( "Status command response: %d Unexpected response from Registrar.", response.status_code) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return None if ("results" not in response_body) or ("uuids" not in response_body["results"]): logger.critical( "Error: unexpected http response body from Registrar: %s", response.status_code) return None agent_list = response_body["results"]["uuids"] # Loop through each agent and ask for status agents = {} for agent in agent_list: agents[agent] = await self.get_agent_state(agent_id) # Pre-create sorted agents list sorted_by_state = {} for state in states.VALID_STATES: sorted_by_state[state] = {} # Build sorted agents list for agent_id in agents: state = agents[agent_id]["operational_state"] sorted_by_state[state][agent_id] = agents[agent_id] print_order = [ states.TENANT_FAILED, states.INVALID_QUOTE, states.FAILED, states.GET_QUOTE, states.GET_QUOTE_RETRY, states.PROVIDE_V, states.PROVIDE_V_RETRY, states.SAVED, states.START, states.TERMINATED, states.REGISTERED ] sorted_agents = [] for state in print_order: for agent_id in sorted_by_state[state]: sorted_agents.append(agent_id) config.echo_json_response(self, 200, "Success", {'uuids': sorted_agents})
async def get(self): """This method handles the GET requests to retrieve status on agents from the WebApp. Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's will return errors. """ rest_params = config.get_restful_params(self.request.uri) if rest_params is None: config.echo_json_response( self, 405, "Not Implemented: Use /agents/ or /logs/ interface") return if "logs" in rest_params and rest_params["logs"] == "tenant": offset = 0 if "pos" in rest_params and rest_params["pos"] is not None and rest_params["pos"].isdigit(): offset = int(rest_params["pos"]) # intercept requests for logs with open(keylime_logging.LOGSTREAM, 'r') as f: logValue = f.readlines() config.echo_json_response(self, 200, "Success", { 'log': logValue[offset:]}) return if "agents" not in rest_params: # otherwise they must be looking for agent info config.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: %s', self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # Handle request for specific agent data separately agents = await self.get_agent_state(agent_id) agents["id"] = agent_id config.echo_json_response(self, 200, "Success", agents) return # If no agent ID, get list of all agents from Registrar try: get_agents = RequestsClient(registrar_base_tls_url, tls_enabled) response = get_agents.get( ('/agents/'), cert=cert, verify=False ) except Exception as e: logger.error("Status command response: %s:%s Unexpected response from Registrar.", tenant_templ.registrar_ip, tenant_templ.registrar_port) logger.exception(e) config.echo_json_response( self, 500, "Unexpected response from Registrar", str(e)) return response_body = response.json() if response.status_code != 200: logger.error("Status command response: %d Unexpected response from Registrar.", response.status_code) keylime_logging.log_http_response( logger, logging.ERROR, response_body) return None if ("results" not in response_body) or ("uuids" not in response_body["results"]): logger.critical("Error: unexpected http response body from Registrar: %s", response.status_code) return None agent_list = response_body["results"]["uuids"] config.echo_json_response(self, 200, "Success", { 'uuids': agent_list})
def do_quote(self): """initiaite v, agent_id and ip initiate the cloudinit sequence""" self.nonce = TPM_Utilities.random_password(20) numtries = 0 response = None # Note: We need a specific retry handler (perhaps in common), no point having localised unless we have too. while True: try: #params = '/quotes/identity?nonce=%s'%(self.nonce) params = f'/quotes/identity?nonce={self.nonce}' response = httpclient_requests.request("GET", "%s" % (self.cloudagent_ip), self.cloudagent_port, params=params, context=None) response_body = json.loads(response.read().decode()) except Exception as e: if response == 503 or response == 504: numtries += 1 maxr = config.getint('tenant', 'max_retries') if numtries >= maxr: logger.error( f"tenant cannot establish connection to agent on {self.cloudagent_ip} with port {self.cloudagent_port}" ) exit() retry = config.getfloat('tenant', 'retry_interval') logger.info( f"tenant connection to agent at {self.cloudagent_ip} refused {numtries}/{maxr} times, trying again in {retry} seconds..." ) time.sleep(retry) continue else: raise (e) break try: if response is not None and response.status != 200: raise UserError( "Status command response: %d Unexpected response from Cloud Agent." % response.status) if "results" not in response_body: raise UserError( "Error: unexpected http response body from Cloud Agent: %s" % str(response.status)) quote = response_body["results"]["quote"] logger.debug(f"agent_quote received quote: {quote}") public_key = response_body["results"]["pubkey"] logger.debug(f"agent_quote received public key: {public_key}") # Get tpm_version, hash_alg tpm_version = response_body["results"]["tpm_version"] logger.debug( f"agent_quote received tpm version: {str(tpm_version)}") # Ensure hash_alg is in accept_tpm_hash_algs list hash_alg = response_body["results"]["hash_alg"] logger.debug(f"agent_quote received hash algorithm: {hash_alg}") if not Hash_Algorithms.is_accepted( hash_alg, config.get('tenant', 'accept_tpm_hash_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted hash algorithm: %s" % hash_alg) # Ensure enc_alg is in accept_tpm_encryption_algs list enc_alg = response_body["results"]["enc_alg"] logger.debug( f"agent_quote received encryption algorithm: {enc_alg}") if not Encrypt_Algorithms.is_accepted( enc_alg, config.get('tenant', 'accept_tpm_encryption_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted encryption algorithm: %s" % enc_alg) # Ensure sign_alg is in accept_tpm_encryption_algs list sign_alg = response_body["results"]["sign_alg"] logger.debug(f"agent_quote received signing algorithm: {sign_alg}") if not Sign_Algorithms.is_accepted( sign_alg, config.get('tenant', 'accept_tpm_signing_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted signing algorithm: %s" % sign_alg) if not self.validate_tpm_quote(public_key, quote, tpm_version, hash_alg): raise UserError( "TPM Quote from cloud agent is invalid for nonce: %s" % self.nonce) logger.info(f"Quote from {self.cloudagent_ip} validated") # encrypt U with the public key # encrypted_U = crypto.rsa_encrypt(crypto.rsa_import_pubkey(public_key),str(self.U)) encrypted_U = crypto.rsa_encrypt( crypto.rsa_import_pubkey(public_key), self.U) b64_encrypted_u = base64.b64encode(encrypted_U) logger.debug("b64_encrypted_u: " + b64_encrypted_u.decode('utf-8')) data = { 'encrypted_key': b64_encrypted_u, 'auth_tag': self.auth_tag } if self.payload is not None: data['payload'] = self.payload u_json_message = json.dumps(data) # post encrypted U back to CloudAgent params = '/keys/ukey' response = httpclient_requests.request("POST", "%s" % (self.cloudagent_ip), self.cloudagent_port, params=params, data=u_json_message) if response == 503: logger.error( f"Cannot connect to Agent at {self.cloudagent_ip} with Port {self.cloudagent_port}. Connection refused." ) exit() elif response == 504: logger.error( f"Verifier at {self.cloudverifier_ip} with Port {self.cloudverifier_port} timed out." ) exit() if response.status != 200: keylime_logging.log_http_response(logger, logging.ERROR, response_body) raise UserError( "Posting of Encrypted U to the Cloud Agent failed with response code %d" % response.status) except Exception as e: self.do_cvstop() raise e
def do_quote(self): """ Perform TPM quote by GET towards Agent Raises: UserError: Connection handler """ self.nonce = TPM_Utilities.random_password(20) numtries = 0 response = None # Note: We need a specific retry handler (perhaps in common), no point having localised unless we have too. while True: try: params = '/quotes/identity?nonce=%s' % (self.nonce) cloudagent_base_url = f'{self.agent_ip}:{self.agent_port}' do_quote = RequestsClient(cloudagent_base_url, tls_enabled=False) response = do_quote.get( params, cert=self.cert ) response_body = response.json() except Exception as e: if response.status_code in (503, 504): numtries += 1 maxr = config.getint('tenant', 'max_retries') if numtries >= maxr: logger.error("Tenant cannot establish connection to agent on %s with port %s", self.agent_ip, self.agent_port) sys.exit() retry = config.getfloat('tenant', 'retry_interval') logger.info("Tenant connection to agent at %s refused %s/%s times, trying again in %s seconds...", self.agent_ip, numtries, maxr, retry) time.sleep(retry) continue raise e break try: if response is not None and response.status_code != 200: raise UserError( "Status command response: %d Unexpected response from Cloud Agent." % response.status) if "results" not in response_body: raise UserError( "Error: unexpected http response body from Cloud Agent: %s" % str(response.status)) quote = response_body["results"]["quote"] logger.debug("Agent_quote received quote: %s", quote) public_key = response_body["results"]["pubkey"] logger.debug("Agent_quote received public key: %s", public_key) # Ensure hash_alg is in accept_tpm_hash_algs list hash_alg = response_body["results"]["hash_alg"] logger.debug("Agent_quote received hash algorithm: %s", hash_alg) if not algorithms.is_accepted(hash_alg, config.get('tenant', 'accept_tpm_hash_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted hash algorithm: %s" % hash_alg) # Ensure enc_alg is in accept_tpm_encryption_algs list enc_alg = response_body["results"]["enc_alg"] logger.debug("Agent_quote received encryption algorithm: %s", enc_alg) if not algorithms.is_accepted(enc_alg, config.get('tenant', 'accept_tpm_encryption_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted encryption algorithm: %s" % enc_alg) # Ensure sign_alg is in accept_tpm_encryption_algs list sign_alg = response_body["results"]["sign_alg"] logger.debug("Agent_quote received signing algorithm: %s", sign_alg) if not algorithms.is_accepted(sign_alg, config.get('tenant', 'accept_tpm_signing_algs').split(',')): raise UserError( "TPM Quote is using an unaccepted signing algorithm: %s" % sign_alg) if not self.validate_tpm_quote(public_key, quote, hash_alg): raise UserError( "TPM Quote from cloud agent is invalid for nonce: %s" % self.nonce) logger.info("Quote from %s validated", self.agent_ip) # encrypt U with the public key encrypted_U = crypto.rsa_encrypt( crypto.rsa_import_pubkey(public_key), self.U) b64_encrypted_u = base64.b64encode(encrypted_U) logger.debug("b64_encrypted_u: %s", b64_encrypted_u.decode('utf-8')) data = { 'encrypted_key': b64_encrypted_u, 'auth_tag': self.auth_tag } if self.payload is not None: data['payload'] = self.payload u_json_message = json.dumps(data) # post encrypted U back to CloudAgent params = '/keys/ukey' cloudagent_base_url = ( f'{self.agent_ip}:{self.agent_port}' ) post_ukey = RequestsClient(cloudagent_base_url, tls_enabled=False) response = post_ukey.post( params, data=u_json_message ) if response.status_code == 503: logger.error("Cannot connect to Agent at %s with Port %s. Connection refused.", self.agent_ip, self.agent_port) sys.exit() elif response.status_code == 504: logger.error("Verifier at %s with Port %s timed out.", self.verifier_ip, self.verifier_port) sys.exit() if response.status_code != 200: keylime_logging.log_http_response( logger, logging.ERROR, response_body) raise UserError( "Posting of Encrypted U to the Cloud Agent failed with response code %d" % response.status) except Exception as e: self.do_cvstop() raise e
def doRegisterAgent(registrar_ip, registrar_port, agent_id, ek_tpm, ekcert, aik_tpm, mtls_cert=None, contact_ip=None, contact_port=None): data = { "ekcert": ekcert, "aik_tpm": aik_tpm, } if ekcert is None or ekcert == "emulator": data["ek_tpm"] = ek_tpm if mtls_cert is not None: data["mtls_cert"] = mtls_cert else: logger.error( "Most actions require the agent to have mTLS enabled, but no cert was provided!" ) if contact_ip is not None: data["ip"] = contact_ip if contact_port is not None: data["port"] = contact_port response = None try: client = RequestsClient(f"{registrar_ip}:{registrar_port}", tls_enabled, ignore_hostname=True) response = client.post(f"/v{api_version}/agents/{agent_id}", cert=tls_cert_info, data=json.dumps(data), verify=ca_cert) response_body = response.json() if response.status_code != 200: logger.error( "Error: unexpected http response code from Registrar Server: %s", response.status_code) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return None logger.info("Agent registration requested for %s", agent_id) if "results" not in response_body: logger.critical( "Error: unexpected http response body from Registrar Server: %s", response.status_code) return None if "blob" not in response_body["results"]: logger.critical( "Error: did not receive blob from Registrar Server: %s", response.status_code) return None return response_body["results"]["blob"] except Exception as e: if response and response.status_code == 503: logger.error( "Agent cannot establish connection to registrar at %s:%s", registrar_ip, registrar_port) sys.exit() else: logger.exception(e) return None
async def get(self): """This method handles the GET requests to retrieve status on agents from the WebApp. Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's will return errors. """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ or /logs/ interface") return if "logs" in rest_params and rest_params["logs"] == "tenant": offset = 0 if "pos" in rest_params and rest_params[ "pos"] is not None and rest_params["pos"].isdigit(): offset = int(rest_params["pos"]) # intercept requests for logs with open(keylime_logging.LOGSTREAM, 'r') as f: logValue = f.readlines() common.echo_json_response(self, 200, "Success", {'log': logValue[offset:]}) return elif "agents" not in rest_params: # otherwise they must be looking for agent info common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # Handle request for specific agent data separately agents = await self.get_agent_state(agent_id) agents["id"] = agent_id common.echo_json_response(self, 200, "Success", agents) return # If no agent ID, get list of all agents from Registrar try: res = tornado_requests.request( "GET", "http://%s:%s/agents/" % (tenant_templ.registrar_ip, tenant_templ.registrar_port), context=tenant_templ.context) response = await res except Exception as e: logger.error( "Status command response: %s:%s Unexpected response from Registrar." % (tenant_templ.registrar_ip, tenant_templ.registrar_port)) logger.exception(e) common.echo_json_response(self, 500, "Unexpected response from Registrar", str(e)) return response_body = response.json() if response.status_code != 200: logger.error( "Status command response: %d Unexpected response from Registrar." % response.status_code) keylime_logging.log_http_response(logger, logging.ERROR, response_body) return None if ("results" not in response_body) or ("uuids" not in response_body["results"]): logger.critical( "Error: unexpected http response body from Registrar: %s" % str(response.status_code)) return None agent_list = response_body["results"]["uuids"] # Loop through each agent and ask for status agents = {} for agent in agent_list: agents[agent] = await self.get_agent_state(agent_id) # Pre-create sorted agents list sorted_by_state = {} states = cloud_verifier_common.CloudAgent_Operational_State.STR_MAPPINGS for state in states: sorted_by_state[state] = {} # Build sorted agents list for agent_id in agents: state = agents[agent_id]["operational_state"] sorted_by_state[state][agent_id] = agents[agent_id] print_order = [10, 9, 7, 3, 4, 5, 6, 2, 1, 8, 0] sorted_agents = [] for state in print_order: for agent_id in sorted_by_state[state]: sorted_agents.append(agent_id) common.echo_json_response(self, 200, "Success", {'uuids': sorted_agents})