Loading services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py +107 −3 Original line number Original line Diff line number Diff line Loading @@ -5,9 +5,13 @@ from datetime import datetime, timedelta import rfc3987 import rfc3987 from bson import json_util from bson import json_util from flask import current_app from flask import current_app, request from flask_jwt_extended import create_access_token from flask_jwt_extended import create_access_token from pymongo import ReturnDocument from pymongo import ReturnDocument import hmac import hashlib import unicodedata from ..core.publisher import Publisher from ..core.publisher import Publisher from ..models.access_token_claims import AccessTokenClaims from ..models.access_token_claims import AccessTokenClaims Loading Loading @@ -82,6 +86,47 @@ class SecurityOperations(Resource): token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope") token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope") return make_response(object=clean_empty(token_error.to_dict()), status=400) return make_response(object=clean_empty(token_error.to_dict()), status=400) def __derive_psk(self, master_key:str, session_id:str, interface:dict): ## Derive the PSK using the provided master key, session ID, and interface information # Interface information host = None if 'fqdn' in interface: host = interface['fqdn'] elif 'ipv4Addr' in interface: host = interface['ipv4Addr'] elif 'ipv6Addr' in interface: host = interface['ipv6Addr'] port = interface.get('port', None) api_prefix = interface.get('apiPrefix', '') scheme = "https" if port in (None, 443) else "http" interface_info = f"{scheme}://{host}" if port and port != 443: interface_info += f":{port}" interface_info += api_prefix # Normalize the strings to NFKC form p0_string = unicodedata.normalize("NFKC", interface_info).encode("utf-8") p1_string = unicodedata.normalize("NFKC", session_id).encode("utf-8") # Convert to octet format (0xFF) p0_octet_string = ' '.join(f'0x{byte:02X}' for byte in p0_string) p1_octet_string = ' '.join(f'0x{byte:02X}' for byte in p1_string) # Convert number of bytes to 16-bit big-endian l0 = ' '.join(f'0x{byte:02X}' for byte in len(p0_octet_string).to_bytes(2, 'big')) l1 = ' '.join(f'0x{byte:02X}' for byte in len(p1_octet_string).to_bytes(2, 'big')) # Create S string using FC (0x7A) and the octet strings with their lengths S = "0x7A" + ' ' + p0_octet_string + ' ' + l0 + ' ' + p1_octet_string + ' ' + l1 psk = hmac.new(master_key.encode("utf-8"), S.encode("utf-8"), hashlib.sha256).digest() return psk def __init__(self): def __init__(self): Resource.__init__(self) Resource.__init__(self) self.filter_aef_id = "aef_profiles.aef_id" self.filter_aef_id = "aef_profiles.aef_id" Loading Loading @@ -259,7 +304,7 @@ class SecurityOperations(Resource): self.db.capif_service_col) self.db.capif_service_col) services_security_object = capif_service_col.find_one( services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) current_app.logger.debug("Aef profile: " + str(services_security_object)) if services_security_object is None: if services_security_object is None: current_app.logger.error( current_app.logger.error( "Not found service with this aef id: " + service_instance.aef_id) "Not found service with this aef id: " + service_instance.aef_id) Loading Loading @@ -291,6 +336,36 @@ class SecurityOperations(Resource): # Select the highest-priority security method # Select the highest-priority security method service_instance.sel_security_method = sorted_methods[0] service_instance.sel_security_method = sorted_methods[0] if service_instance.sel_security_method == "PSK": request.headers.get('X-TLS-Protocol', 'N/A') sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') Mkey = request.headers.get('X-TLS-MKey', 'N/A') current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") interface = None if service_instance.interface_details: current_app.logger.debug("Interface details found") interface = service_instance.interface_details.to_dict() else: current_app.logger.error("Interface details not found") services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0}) current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0])) if "interface_descriptions" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"])) interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0] elif "domain_name" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"])) interface = services_security_object["aef_profiles"][0]["domain_name"] if interface: current_app.logger.debug("Deriving PSK") psk = self.__derive_psk(Mkey, sesionId, interface) current_app.logger.debug("PSK derived : " + str(psk)) service_instance.authorization_info = str(psk) # Send service instance to ACL # Send service instance to ACL current_app.logger.debug("Sending message to create ACL") current_app.logger.debug("Sending message to create ACL") publish_ops.publish_message("acls-messages", "create-acl:"+str( publish_ops.publish_message("acls-messages", "create-acl:"+str( Loading Loading @@ -483,6 +558,35 @@ class SecurityOperations(Resource): valid_security_method)[0] valid_security_method)[0] update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id}) update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id}) if service_instance.sel_security_method == "PSK": request.headers.get('X-TLS-Protocol', 'N/A') sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') Mkey = request.headers.get('X-TLS-MKey', 'N/A') current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") interface = None if service_instance.interface_details: current_app.logger.debug("Interface details found") interface = service_instance.interface_details.to_dict() else: current_app.logger.error("Interface details not found") services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0}) current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0])) if "interface_descriptions" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"])) interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0] elif "domain_name" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"])) interface = services_security_object["aef_profiles"][0]["domain_name"] if interface: current_app.logger.debug("Deriving PSK") psk = self.__derive_psk(Mkey, sesionId, interface) current_app.logger.debug("PSK derived : " + str(psk)) service_instance.authorization_info = str(psk) service_security = service_security.to_dict() service_security = service_security.to_dict() service_security = clean_empty(service_security) service_security = clean_empty(service_security) Loading services/docker-compose-capif.yml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -306,7 +306,7 @@ services: ports: ports: - "8080:8080" - "8080:8080" - "443:443" - "443:443" image: ${REGISTRY_BASE_URL}/nginx:${OCF_VERSION} image: pelayo222/mi-nginx:1.27.1-arm64 environment: environment: - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} - VAULT_HOSTNAME=vault - VAULT_HOSTNAME=vault Loading services/nginx/Dockerfile +1 −1 Original line number Original line Diff line number Diff line FROM labs.etsi.org:5050/ocf/capif/nginx:1.27.1 FROM pelayo222/mi-nginx:1.27.1-arm64 RUN apt-get update && apt-get install -y jq && apt-get clean RUN apt-get update && apt-get install -y jq && apt-get clean RUN apt-get install -y openssl RUN apt-get install -y openssl RUN apt-get install -y curl RUN apt-get install -y curl Loading services/nginx/nginx.conf +4 −0 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,10 @@ http { return 401 $security_error_message; return 401 $security_error_message; } } proxy_set_header X-TLS-Protocol $ssl_protocol; proxy_set_header X-TLS-Session-ID $ssl_session_id; proxy_set_header X-TLS-MKey $sslkeylog_mk; proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_pass http://capif-security:8080; proxy_pass http://capif-security:8080; } } Loading Loading
services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py +107 −3 Original line number Original line Diff line number Diff line Loading @@ -5,9 +5,13 @@ from datetime import datetime, timedelta import rfc3987 import rfc3987 from bson import json_util from bson import json_util from flask import current_app from flask import current_app, request from flask_jwt_extended import create_access_token from flask_jwt_extended import create_access_token from pymongo import ReturnDocument from pymongo import ReturnDocument import hmac import hashlib import unicodedata from ..core.publisher import Publisher from ..core.publisher import Publisher from ..models.access_token_claims import AccessTokenClaims from ..models.access_token_claims import AccessTokenClaims Loading Loading @@ -82,6 +86,47 @@ class SecurityOperations(Resource): token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope") token_error = AccessTokenErr(error="invalid_scope", error_description="malformed scope") return make_response(object=clean_empty(token_error.to_dict()), status=400) return make_response(object=clean_empty(token_error.to_dict()), status=400) def __derive_psk(self, master_key:str, session_id:str, interface:dict): ## Derive the PSK using the provided master key, session ID, and interface information # Interface information host = None if 'fqdn' in interface: host = interface['fqdn'] elif 'ipv4Addr' in interface: host = interface['ipv4Addr'] elif 'ipv6Addr' in interface: host = interface['ipv6Addr'] port = interface.get('port', None) api_prefix = interface.get('apiPrefix', '') scheme = "https" if port in (None, 443) else "http" interface_info = f"{scheme}://{host}" if port and port != 443: interface_info += f":{port}" interface_info += api_prefix # Normalize the strings to NFKC form p0_string = unicodedata.normalize("NFKC", interface_info).encode("utf-8") p1_string = unicodedata.normalize("NFKC", session_id).encode("utf-8") # Convert to octet format (0xFF) p0_octet_string = ' '.join(f'0x{byte:02X}' for byte in p0_string) p1_octet_string = ' '.join(f'0x{byte:02X}' for byte in p1_string) # Convert number of bytes to 16-bit big-endian l0 = ' '.join(f'0x{byte:02X}' for byte in len(p0_octet_string).to_bytes(2, 'big')) l1 = ' '.join(f'0x{byte:02X}' for byte in len(p1_octet_string).to_bytes(2, 'big')) # Create S string using FC (0x7A) and the octet strings with their lengths S = "0x7A" + ' ' + p0_octet_string + ' ' + l0 + ' ' + p1_octet_string + ' ' + l1 psk = hmac.new(master_key.encode("utf-8"), S.encode("utf-8"), hashlib.sha256).digest() return psk def __init__(self): def __init__(self): Resource.__init__(self) Resource.__init__(self) self.filter_aef_id = "aef_profiles.aef_id" self.filter_aef_id = "aef_profiles.aef_id" Loading Loading @@ -259,7 +304,7 @@ class SecurityOperations(Resource): self.db.capif_service_col) self.db.capif_service_col) services_security_object = capif_service_col.find_one( services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) {"api_id": service_instance.api_id, self.filter_aef_id: service_instance.aef_id}, {"aef_profiles.security_methods.$": 1}) current_app.logger.debug("Aef profile: " + str(services_security_object)) if services_security_object is None: if services_security_object is None: current_app.logger.error( current_app.logger.error( "Not found service with this aef id: " + service_instance.aef_id) "Not found service with this aef id: " + service_instance.aef_id) Loading Loading @@ -291,6 +336,36 @@ class SecurityOperations(Resource): # Select the highest-priority security method # Select the highest-priority security method service_instance.sel_security_method = sorted_methods[0] service_instance.sel_security_method = sorted_methods[0] if service_instance.sel_security_method == "PSK": request.headers.get('X-TLS-Protocol', 'N/A') sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') Mkey = request.headers.get('X-TLS-MKey', 'N/A') current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") interface = None if service_instance.interface_details: current_app.logger.debug("Interface details found") interface = service_instance.interface_details.to_dict() else: current_app.logger.error("Interface details not found") services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0}) current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0])) if "interface_descriptions" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"])) interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0] elif "domain_name" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"])) interface = services_security_object["aef_profiles"][0]["domain_name"] if interface: current_app.logger.debug("Deriving PSK") psk = self.__derive_psk(Mkey, sesionId, interface) current_app.logger.debug("PSK derived : " + str(psk)) service_instance.authorization_info = str(psk) # Send service instance to ACL # Send service instance to ACL current_app.logger.debug("Sending message to create ACL") current_app.logger.debug("Sending message to create ACL") publish_ops.publish_message("acls-messages", "create-acl:"+str( publish_ops.publish_message("acls-messages", "create-acl:"+str( Loading Loading @@ -483,6 +558,35 @@ class SecurityOperations(Resource): valid_security_method)[0] valid_security_method)[0] update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id}) update_acls.append({"api_id": service_instance.api_id, "aef_id": service_instance.aef_id}) if service_instance.sel_security_method == "PSK": request.headers.get('X-TLS-Protocol', 'N/A') sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') Mkey = request.headers.get('X-TLS-MKey', 'N/A') current_app.logger.info(f"TLS Protocol: {request.headers.get('X-TLS-Protocol', 'N/A')}, Session id: {sesionId}, Master Key: {Mkey}") interface = None if service_instance.interface_details: current_app.logger.debug("Interface details found") interface = service_instance.interface_details.to_dict() else: current_app.logger.error("Interface details not found") services_security_object = capif_service_col.find_one( {"api_id": service_instance.api_id}, {"aef_profiles": {"$elemMatch": {"aef_id": service_instance.aef_id}}, "_id": 0}) current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0])) if "interface_descriptions" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["interface_descriptions"])) interface = services_security_object["aef_profiles"][0]["interface_descriptions"][0] elif "domain_name" in services_security_object["aef_profiles"][0]: current_app.logger.debug("Aef profile: " + str(services_security_object["aef_profiles"][0]["domain_name"])) interface = services_security_object["aef_profiles"][0]["domain_name"] if interface: current_app.logger.debug("Deriving PSK") psk = self.__derive_psk(Mkey, sesionId, interface) current_app.logger.debug("PSK derived : " + str(psk)) service_instance.authorization_info = str(psk) service_security = service_security.to_dict() service_security = service_security.to_dict() service_security = clean_empty(service_security) service_security = clean_empty(service_security) Loading
services/docker-compose-capif.yml +1 −1 Original line number Original line Diff line number Diff line Loading @@ -306,7 +306,7 @@ services: ports: ports: - "8080:8080" - "8080:8080" - "443:443" - "443:443" image: ${REGISTRY_BASE_URL}/nginx:${OCF_VERSION} image: pelayo222/mi-nginx:1.27.1-arm64 environment: environment: - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} - VAULT_HOSTNAME=vault - VAULT_HOSTNAME=vault Loading
services/nginx/Dockerfile +1 −1 Original line number Original line Diff line number Diff line FROM labs.etsi.org:5050/ocf/capif/nginx:1.27.1 FROM pelayo222/mi-nginx:1.27.1-arm64 RUN apt-get update && apt-get install -y jq && apt-get clean RUN apt-get update && apt-get install -y jq && apt-get clean RUN apt-get install -y openssl RUN apt-get install -y openssl RUN apt-get install -y curl RUN apt-get install -y curl Loading
services/nginx/nginx.conf +4 −0 Original line number Original line Diff line number Diff line Loading @@ -142,6 +142,10 @@ http { return 401 $security_error_message; return 401 $security_error_message; } } proxy_set_header X-TLS-Protocol $ssl_protocol; proxy_set_header X-TLS-Session-ID $ssl_session_id; proxy_set_header X-TLS-MKey $sslkeylog_mk; proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_pass http://capif-security:8080; proxy_pass http://capif-security:8080; } } Loading