diff --git a/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py b/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py index 385d3cfd4a8c6bb03a4f517c8863e1e0141dec3c..f38d12f674757cc03639a0acc46d818acbe72b14 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/controllers/default_controller.py @@ -164,7 +164,6 @@ def trusted_invokers_api_invoker_id_put(api_invoker_id, body): # noqa: E501 :rtype: Union[ServiceSecurity, Tuple[ServiceSecurity, int], Tuple[ServiceSecurity, int, Dict[str, str]] """ - current_app.logger.info("Creating security context") if request.is_json: body = ServiceSecurity.from_dict(request.get_json()) # noqa: E501 diff --git a/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py b/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py index 2cbc3e7779d87215267a2c85fd94e0ddc464710f..e4f1cfa74f98b294f367dacfdb0b2d0c1c8ac033 100644 --- a/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py +++ b/services/TS29222_CAPIF_Security_API/capif_security/core/servicesecurity.py @@ -5,7 +5,7 @@ from datetime import datetime, timedelta import rfc3987 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 pymongo import ReturnDocument @@ -18,6 +18,10 @@ from .redis_event import RedisEvent from .resources import Resource from .responses import bad_request_error, forbidden_error, internal_server_error, make_response, not_found_error +import hmac +import hashlib +import unicodedata + publish_ops = Publisher() security_context_not_found_detail = "Security context not found" @@ -26,6 +30,26 @@ api_invoker_no_context_cause = "API Invoker has no security context" class SecurityOperations(Resource): + def __derive_psk(self, master_key, session_id, interface_info): + + 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 __check_invoker(self, api_invoker_id): invokers_col = self.db.get_col_by_name(self.db.capif_invokers) @@ -201,13 +225,17 @@ class SecurityOperations(Resource): services_security_object = mycol.find_one( {"api_invoker_id": api_invoker_id}) + current_app.logger.debug("HERE") + if services_security_object is not None: current_app.logger.error( "Already security context defined with same api invoker id") return forbidden_error(detail="Security method already defined", cause="Identical AEF Profile IDs") - + + current_app.logger.debug(service_security.security_info) for service_instance in service_security.security_info: + current_app.logger.debug(service_instance.interface_details) if service_instance.interface_details is not None: # We look for if the passed interface exists for the given apiId @@ -253,13 +281,14 @@ class SecurityOperations(Resource): pref_security_methods = service_instance.pref_security_methods valid_security_method = set( security_methods) & set(pref_security_methods) + current_app.logger.debug(service_instance.interface_details) else: capif_service_col = self.db.get_col_by_name( self.db.capif_service_col) 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}) - + current_app.logger.debug("Aef profile: " + str(services_security_object)) if services_security_object is None: current_app.logger.error( "Not found service with this aef id: " + service_instance.aef_id) @@ -291,6 +320,24 @@ class SecurityOperations(Resource): # Select the highest-priority security method service_instance.sel_security_method = sorted_methods[0] + if service_instance.sel_security_method == "PSK": + current_app.logger.info(request.headers.get('X-TLS-Protocol', 'N/A')) + current_app.logger.info("Creating security context") + sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') + current_app.logger.info(f"TLS Session ID: {sesionId}") + + Mkey = request.headers.get('X-TLS-MKey', 'N/A') + current_app.logger.info(f"TLS MKey: {Mkey}") + + if service_instance.interface_details is not None: + interface_info = ( + getattr(service_instance.interface_details, 'ipv4_addr', None) or + getattr(service_instance.interface_details, 'ipv6_addr', None) or + getattr(service_instance.interface_details, 'fqdn', None) + ) + else: + services_security_object + # Send service instance to ACL current_app.logger.debug("Sending message to create ACL") publish_ops.publish_message("acls-messages", "create-acl:"+str( @@ -300,6 +347,16 @@ class SecurityOperations(Resource): rec = dict() rec['api_invoker_id'] = api_invoker_id + + if service_instance.sel_security_method == "PSK": + current_app.logger.info("Deriving PSK") + sesionId = request.headers.get('X-TLS-Session-ID', 'N/A') + Mkey = request.headers.get('X-TLS-MKey', 'N/A') + + invoker_psk = self.__derive_psk(Mkey, sesionId, interface_info) + current_app.logger.info(f"Derived PSK: {invoker_psk.hex()}") + rec['invoker_psk'] = invoker_psk + rec.update(service_security.to_dict()) mycol.insert_one(rec) diff --git a/services/docker-compose-capif.yml b/services/docker-compose-capif.yml index 06247465c40bca268b9b354de4e0b972600bddf0..2c6f40efdf711ea4321dab7c378494944cd426b8 100644 --- a/services/docker-compose-capif.yml +++ b/services/docker-compose-capif.yml @@ -306,7 +306,7 @@ services: ports: - "8080:8080" - "443:443" - image: ${REGISTRY_BASE_URL}/nginx:${OCF_VERSION} + image: pelayo222/mi-nginx:1.27.1-arm64 environment: - CAPIF_HOSTNAME=${CAPIF_HOSTNAME} - VAULT_HOSTNAME=vault diff --git a/services/nginx/Dockerfile b/services/nginx/Dockerfile index c87732c5ba562af7dac31974b62bc560a47faef1..d34d8b94a05a5a4abe511f3c1c4f3bdd4039a9a4 100644 --- a/services/nginx/Dockerfile +++ b/services/nginx/Dockerfile @@ -1,4 +1,4 @@ -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 install -y openssl RUN apt-get install -y curl diff --git a/services/nginx/nginx-sslkeylog b/services/nginx/nginx-sslkeylog new file mode 160000 index 0000000000000000000000000000000000000000..f0356898000b771ce6bbaa48586c85c6d72904ec --- /dev/null +++ b/services/nginx/nginx-sslkeylog @@ -0,0 +1 @@ +Subproject commit f0356898000b771ce6bbaa48586c85c6d72904ec diff --git a/services/nginx/nginx.conf b/services/nginx/nginx.conf index f51e177fd0fce53f6d151e3483fcd4802a5a1d3c..b1ca67c428e6ad97b589448a9044cdd50ebde029 100644 --- a/services/nginx/nginx.conf +++ b/services/nginx/nginx.conf @@ -7,6 +7,7 @@ events { worker_connections 1024; } + http { map $ssl_client_s_dn $ssl_client_s_dn_cn { default ""; @@ -70,6 +71,8 @@ http { ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client optional; ssl_verify_depth 2; + + ssl_session_tickets off; location / { proxy_pass $scheme://$http_host/api-invoker-management/v1/ui/; @@ -101,6 +104,7 @@ http { add_header Content-Type 'application/problem+json'; return 401 $discover_error_message; } + proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_pass http://service-apis:8080; } @@ -113,6 +117,7 @@ http { add_header Content-Type 'application/problem+json'; return 401 $publish_error_message; } + proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_pass http://published-apis:8080; } @@ -142,6 +147,10 @@ http { 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_pass http://capif-security:8080; } diff --git a/services/register/config.yaml.bak b/services/register/config.yaml.bak new file mode 100644 index 0000000000000000000000000000000000000000..fb61459d43bb46e7eb150eefca9c18e94686d82b --- /dev/null +++ b/services/register/config.yaml.bak @@ -0,0 +1,6 @@ +mongo: {'user': 'root', 'password': 'example', 'db': 'capif_users', 'col': 'user', 'admins': 'admins', 'host': 'mongo_register', 'port': '27017'} +ca_factory: {"url": "vault", "port": "8200", "token": "dev-only-token", "verify": False} +ccf: {"url": "capifcore", "helper_remove_user": "/helper/deleteEntities/"} +register: {"register_uuid": '6ba7b810-9dad-11d1-80b4-00c04fd430c8', "refresh_expiration": 30, #days + "token_expiration": 10, #mins + "admin_users": {admin_user: "admin", admin_pass: "password123"}}