Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

copy in 4.2.8.2 files #639

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions docs/In-Band Manageability Installation Guide Ubuntu.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ Intel In-band Manageability framework, a.k.a. INBM, is designed to provide certa

- Ubuntu 22.04 (Desktop and Server)

- Ubuntu 24.04 (Desktop and Server)

- Yocto OS

- Debian 10
Expand Down
34 changes: 0 additions & 34 deletions docs/In-Band Manageability User Guide - INBS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,3 @@ To disable TLS (for example, for testing), set "tls_enabled" to false. tls_cert_
- **tls_enabled**: Boolean value to enable or disable TLS. Set to `true` to enable TLS.
- **tls_cert_path**: (ignored if TLS is disabled) The system path to the TLS certificate used for establishing a secure connection to the INBS server. It is recommended that the path be under `/etc/intel-manageability/public/` to ensure the certificate is accessible to the cloudadapter agent.
- **token_path**: (ignored if TLS is disabled) Path to the file containing the confidential authentication token used to verify the device with the INBS server. It is recommended that the path be under `/etc/intel-manageability/secret/` to ensure the token is secure.

### UDM Bootstrap Mode

The user can also set up `adapter.cfg` in "UDM Bootstrap" mode, like this:

```
{
"cloud": "inbs",
"config": {
"onboarding_json_file": "/path/to/onboarding.json"
}
}
```

The file `onboarding.json` is provided by UDM to give agents on the edge
node a unified view of the device's credentials and addresses to service
endpoints.

Example:

```
{
"jwt_token": "sample_jwt_token",
"refresh_token": "sample_refresh_token",
"node_id": "sample_node_id",
"services": [
{
"type": "INBS",
"host": "example.com",
"port": "8080"
}
]
}
```
17 changes: 0 additions & 17 deletions inbm/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,14 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## X.X.X - YYYY-MM-DD
### Changed
- (NEXMANAGE-956) Improve failure reason from TC that is logged in Mjunct DB for FOTA
- (NEXMANAGE-1194) provision-tc/mqtt services will now handle UDM
- (NEXMANAGE-1253) Implement INBS (UDM) decommission command, which removes device credentials

### Security
- (NEXMANAGE-1236) Upgrade golang.org/x/net from 0.23.0 to 0.33.0, fixing CVE-2024-45338

### Added
- (NEXMANAGE-1222) Add support for Ubuntu 24.04
- (NEXMANAGE-610, NEXMANAGE-692) Add support to handle OOB AMT RPC activation command

## 4.2.8.2 - 2024-12-18
### Changed
- (NEXMANAGE-1121) Remove mqtt-ca group from /var/cache/manageability/repository-tool directory
- Update CI setup script to install rchardet v1.8.0

## 4.2.8.1 - 2024-12-17
- (NEX-15262) Returns failure reason that matches the format required by MM
- (NEXMANAGE-1102) XML schema validator run into exception when field tag is not complete in manifest

### Added
- (NEXMANAGE-354) Apply protovalidate in common.proto

### Fixed
- (NEXMANAGE-1101) Fix History Log Issue

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,13 @@ def convert_rpc_activate_operation_to_xml_manifest(operation: RpcActivateOperati
# Create the root element
manifest = ET.Element('manifest')
ET.SubElement(manifest, 'type').text = 'cmd'
ET.SubElement(manifest, 'cmd').text = 'rpc'
rpc = ET.SubElement(manifest, 'rpc')

cmd = ET.SubElement(manifest, 'cmd')
header = ET.SubElement(cmd, 'header')
ET.SubElement(header, 'type').text = 'rpc'

type = ET.SubElement(cmd, 'type')
rpc = ET.SubElement(type, 'rpc')

if operation.url:
ET.SubElement(rpc, 'fetch').text = operation.url
Expand Down
142 changes: 46 additions & 96 deletions inbm/cloudadapter-agent/cloudadapter/cloud/adapters/inbs_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
from ...cloud.client.inbs_cloud_client import InbsCloudClient
from ..client.cloud_client import CloudClient
from .adapter import Adapter
from typing import Callable, Optional
from typing import Callable
import logging
import os
import json

logger = logging.getLogger(__name__)

Expand All @@ -30,101 +29,52 @@ def configure(self, configs: dict) -> CloudClient:
@param configs: schema conforming JSON config data
@exception AdapterConfigureError: If configuration fails
"""
onboarding_json_file = configs.get("onboarding_json_file")
if onboarding_json_file:
# New mode using onboarding_json_file
if not os.path.exists(onboarding_json_file):
raise AdapterConfigureError(f"Onboarding JSON file '{onboarding_json_file}' does not exist")

with open(onboarding_json_file, 'r') as f:
onboarding_data = json.load(f)

node_id = onboarding_data.get("node_id")
if not node_id:
raise AdapterConfigureError("Missing node_id in onboarding.json")

services = onboarding_data.get("services", [])
inbs_service = None
for service in services:
if service.get("type") == "INBS":
inbs_service = service
break

if not inbs_service:
raise AdapterConfigureError("No INBS service found in onboarding.json")

hostname = inbs_service.get("host")
if not hostname:
raise AdapterConfigureError("Missing host in INBS service from onboarding.json")

port = inbs_service.get("port")
if not port:
raise AdapterConfigureError("Missing port in INBS service from onboarding.json")
try:
port_int = int(port)
if not (0 < port_int < 65536):
raise AdapterConfigureError("Port number must be between 1 and 65535")
except ValueError:
raise AdapterConfigureError("Port must be an integer")

token = onboarding_data.get("jwt_token")
if not token:
raise AdapterConfigureError("Missing jwt_token in onboarding.json")

tls_cert = None # TLS cert always comes from system
tls_enabled = False # Today, INBS does _not_ support TLS in this mode, but later we will enable it

hostname = configs.get("hostname")
if not hostname:
raise AdapterConfigureError("Missing hostname")

port = configs.get("port")
if not port:
raise AdapterConfigureError("Missing port")
try:
port_int = int(port)
if not (0 < port_int < 65536):
raise AdapterConfigureError("Port number must be between 1 and 65535")
except ValueError:
raise AdapterConfigureError("Port must be an integer")

node_id = configs.get("node_id")
if not node_id:
raise AdapterConfigureError("Missing node_id")

tls_enabled = configs.get("tls_enabled", True)
if tls_enabled:
tls_cert_path = configs.get("tls_cert_path")
if not tls_cert_path or not os.path.exists(tls_cert_path):
raise AdapterConfigureError(
"TLS is enabled but missing or incorrect certificate file path")
with open(tls_cert_path, 'rb') as f:
tls_cert = f.read()

token: str | None = None
token_path: str = configs.get("token_path") # type: ignore
if (not token_path or not os.path.exists(token_path)):
raise AdapterConfigureError(
"TLS is enabled but missing or incorrect token file path")
with open(token_path, 'r') as f:
token = f.read().strip()
else:
# Existing mode
hostname = configs.get("hostname")
if not hostname:
raise AdapterConfigureError("Missing hostname")

port = configs.get("port")
if not port:
raise AdapterConfigureError("Missing port")
try:
port_int = int(port)
if not (0 < port_int < 65536):
raise AdapterConfigureError("Port number must be between 1 and 65535")
except ValueError:
raise AdapterConfigureError("Port must be an integer")

node_id = configs.get("node_id")
if not node_id:
raise AdapterConfigureError("Missing node_id")

tls_enabled = configs.get("tls_enabled", True)
tls_cert = None
token = None

if tls_enabled:
tls_cert_path = configs.get("tls_cert_path")
if not tls_cert_path or not os.path.exists(tls_cert_path):
raise AdapterConfigureError("TLS is enabled but missing or incorrect certificate file path")
with open(tls_cert_path, 'rb') as f:
tls_cert = f.read()

token_path = configs.get("token_path")
if not token_path or not os.path.exists(token_path):
raise AdapterConfigureError("TLS is enabled but missing or incorrect token file path")
with open(token_path, 'r') as f:
token = f.read().strip()
else:
if configs.get("token_path"):
raise AdapterConfigureError("Token path provided but TLS is not enabled")
if configs.get("tls_cert_path"):
raise AdapterConfigureError("TLS cert path provided but TLS is not enabled")

# Initialize the client with the gathered parameters
self._client = InbsCloudClient(
hostname=hostname,
port=port,
node_id=node_id,
token=token,
tls_enabled=tls_enabled,
tls_cert=tls_cert if tls_enabled else None
)
if configs.get("token_path"):
raise AdapterConfigureError("Token path provided but TLS is not enabled")
if configs.get("tls_cert_path"):
raise AdapterConfigureError("TLS cert path provided but TLS is not enabled")

self._client = InbsCloudClient(hostname=hostname,
port=port,
node_id=node_id,
token=token if tls_enabled else None,
tls_enabled=tls_enabled,
tls_cert=tls_cert if tls_enabled else None)
return self._client

def bind_callback(self, name: str, callback: Callable) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from cloudadapter.cloud.adapters.inbs.operation import (
convert_updated_scheduled_operations_to_dispatcher_xml,
)
from cloudadapter.constants import METHOD, DEAD, NODE_UPDATE_JSON_SCHEMA_LOCATION, UDM_ONBOARDING_JSON_PATH
from cloudadapter.constants import METHOD, DEAD, NODE_UPDATE_JSON_SCHEMA_LOCATION
from cloudadapter.exceptions import AuthenticationError, PublishError
from cloudadapter.pb.inbs.v1 import inbs_sb_pb2_grpc, inbs_sb_pb2
from cloudadapter.pb.common.v1 import common_pb2
Expand Down Expand Up @@ -64,18 +64,15 @@ def __init__(
self._disp_state_lock = threading.Lock()

self._metadata: list[tuple[str, str]] = [("node-id", node_id)]

# this code will be used when TLS is available in INBS
# if tls_enabled:
# if token is None:
# raise AuthenticationError("Token is required when TLS is enabled.")
# else:
# self._metadata.append(("authorization", "Bearer " + token))
# # if tls_cert is None, this is OK; implied that we have it installed system wide
# instead, we will simply use the token if it exists

if token is not None:
self._metadata.append(("authorization", "Bearer " + token))
if tls_enabled:
if token is None:
raise AuthenticationError("Token is required when TLS is enabled.")
else:
self._metadata.append(("token", token))
if tls_cert is None:
raise AuthenticationError(
"TLS certificate path is required when TLS is enabled."
)
self._stop_event = threading.Event()

self._grpc_channel: grpc.Channel | None = None # this will get set after connect is called
Expand Down Expand Up @@ -231,25 +228,6 @@ def bind_callback(self, name: str, callback: Callable) -> None:

# for now ignore all callbacks; only Ping is supported
self._callbacks[name] = callback

def decommission(self) -> str: # pragma: no cover
"""Decommission the device by truncating the onboarding.json file containing the device tokens.
Returns an empty string on success, otherwise returns an error string"""
error = "" # default, no error

try:
with open(UDM_ONBOARDING_JSON_PATH, "w") as _:
pass # truncate onboarding.json, which will remove the device's key to access the cloud
except FileNotFoundError:
pass # this is still success
except PermissionError as e:
error = f"permission denied opening onboarding.json: {e}"
logger.error(error)
except OSError as e:
error = f"IO error occurred opening onboarding.json: {e}"
logger.error(error)
return error


def _handle_inbm_command_request(
self, request_queue: queue.Queue[inbs_sb_pb2.HandleINBMCommandRequest | None]
Expand All @@ -270,7 +248,7 @@ def _handle_inbm_command_request(
logger.debug(f"Processing gRPC request: request_id {request_id}")
command_type = item.command.WhichOneof("inbm_command")

if self.get_dispatcher_state() == DEAD and not (command_type in ["ping", "decommission"]):
if self.get_dispatcher_state() == DEAD and command_type != "ping":
logger.error(
f"Dispatcher not in running state. Unable to process request - {request_id}"
)
Expand Down Expand Up @@ -334,24 +312,6 @@ def _handle_inbm_command_request(
yield inbs_sb_pb2.HandleINBMCommandResponse(
request_id=request_id
)
elif command_type == "decommission":
logger.debug(
f"Received decommission command for request_id {request_id}"
)
error = self.decommission()

# Only set the error field if there's an actual error message
if error:
response_error = common_pb2.Error(
message=f"cloudadapter: error decommissioning: {error}"
)
else:
response_error = None

yield inbs_sb_pb2.HandleINBMCommandResponse(
request_id=request_id,
error=response_error,
)
else:
logger.error(
f"Received unknown command {command_type} for request_id {request_id}"
Expand Down Expand Up @@ -384,10 +344,7 @@ def _make_grpc_channel(self) -> grpc.Channel:
if self._tls_enabled:
# Create a secure channel with SSL credentials
logger.debug("Setting up connection to INBS cloud with TLS enabled")
if self._tls_cert is None: # assume cert is installed system wide
credentials = grpc.ssl_channel_credentials()
else:
credentials = grpc.ssl_channel_credentials(root_certificates=self._tls_cert)
credentials = grpc.ssl_channel_credentials(root_certificates=self._tls_cert)
self.channel = grpc.secure_channel(
f"{self._grpc_hostname}:{self._grpc_port}", credentials
)
Expand Down
2 changes: 0 additions & 2 deletions inbm/cloudadapter-agent/cloudadapter/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,3 @@ class METHOD:

NODE_UPDATE_JSON_SCHEMA_LOCATION = str(INTEL_MANAGEABILITY_SHARE_PATH_PREFIX /
'cloudadapter-agent' / 'node_update_schema.json')

UDM_ONBOARDING_JSON_PATH = "/mnt/udm-luks/onboarding.json"
Loading