Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
DBC,GPS and DDS Feeder TLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
erikbosch committed Jun 26, 2023
1 parent e484416 commit e0793a9
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 60 deletions.
31 changes: 31 additions & 0 deletions dbc2val/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ A smaller excerpt from the above sample, with fewer signals.
| - | *KUKSA_ADDRESS* | *[general].ip* | `127.0.0.1` | IP address for Server/Databroker |
| - | *KUKSA_PORT* | *[general].port* | `55555` | Port for Server/Databroker |
| *--tls* | - | *[general].tls* | `False` | Shall tls be used for Server/Databroker connection? |
| | - | *[general].root_ca_path* | *Undefined* | Path to root CA: Only needed if using TLS |
| | - | *[general].tls_server_name* | *Undefined* | TLS server name, may be needed if addressing a server by IP-name |
| - | - | *[general].token* | *Undefined* | Token path. Only needed if Databroker/Server requires authentication |
| - | *VEHICLEDATABROKER_DAPR_APP_ID* | - | - | Add dapr-app-id metadata. Only relevant for KUKSA.val Databroker |
| *--dbc2val /--no-dbc2val* | *USE_DBC2VAL* / *NO_USE_DBC2VAL* | *[can].dbc2val* | dbc2val enabled | Specifies if sending data from CAN to KUKSA.val is enabled. Setting the environment variable to any value is equivalent to activating the switch on the command line.|
Expand All @@ -271,6 +273,35 @@ Configuration options have the following priority (highest at top).
3. configuration file
4. default value

### Using kuksa-client with a server requiring Authorization

The [default configuration file](config/dbc-feeder.ini) does not specify any token to use.
If the KUKSA.val Databroker or KUKSA.val Server requires authorization the `token` attribute in the config file
must be set. The default config file include (commented) values to use if using KUKSA.val example tokens.

*Note: Production deployments are strongly recommend to use Authorization but must NOT use the example tokens available in the KUKSA.val repository!*

### Using kuksa-client with a server requiring TLS

The [default configuration file](config/dbc-feeder.ini) does not specify that TLS shall be used.
If the KUKSA.val Databroker or KUKSA.val Server requires authentication the `tls` attribute in the config file
must be set to `True` and `root_ca_path` must be set.
The default config file include (commented) values to use if using KUKSA.val example certificates.

The feeder verifies that the Databroker/Server presents a certificate with a name matching the server.
The KUKSA.val default server certificate include `Server`, `localhost` and `127.0.0.1` as names, but due to a limitation
name validation does not work when using gRPC and a numeric IP-address, so for that combination you must as a work around
specify the `tls_server_name` to use in name validation, like in the example below.

```
ip = 127.0.0.1
tls = True
root_ca_path=../../kuksa.val/kuksa_certificates/CA.pem
tls_server_name=localhost
```

*Note: Production deployments are strongly recommend to use TLS but must NOT use the example certificates available in the KUKSA.val repository!*

## Using kuksa-val-server

1. To make the feeder communicate with this server, use the `--server-type kuksa_val_server` CLI option or refer to [Configuration](#configuration) for `server-type`.
Expand Down
10 changes: 9 additions & 1 deletion dbc2val/config/dbc_feeder.ini
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,20 @@ port = 55555
tls = False
# tls = True

# TLS-related settings
# Path to root CA, needed if using TLS
root_ca_path=../../kuksa.val/kuksa_certificates/CA.pem
# Server name, typically only needed if accessing server by IP address like 127.0.0.1
# and typically only if connection to KUKSA.val Databroker
# If using KUKSA.val example certificates the names "Server" or "localhost" can be used.
# tls_server_name=localhost

# Token file for authorization.
# Default behavior differ between servers
# For KUKSA.val Databroker the KUKSA.val default token not included in packages and containers
# If you run your Databroker so it require authentication you must specify token
# The example below works if you have cloned kuksa.val in parallel to kuksa.val.feeders
#token=../../kuksa.val/jwt/provide-all.token
# token=../../kuksa.val/jwt/provide-all.token
# For KUKSA.val Server the default behavior is to use the token provided as part of kuksa-client
# So you only need to specify a different token if you want to use a different token
# Possibly like below
Expand Down
13 changes: 13 additions & 0 deletions dbc2val/dbcfeeder.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,19 @@ def main(argv):
if "tls" in config["general"]:
client_wrapper.set_tls(config["general"].getboolean("tls"))

if "root_ca_path" in config["general"]:
path = config['general']['root_ca_path']
log.info(f"Given root CA path: {path}")
client_wrapper.set_root_ca_path(path)
elif client_wrapper.get_tls():
# We do not want to rely on kuksa-client default
log.error("Root CA must be given when using TLS")

if "tls_server_name" in config["general"]:
name = config['general']['tls_server_name']
log.info(f"Given TLS server name: {name}")
client_wrapper.set_tls_server_name(name)

if "token" in config["general"]:
log.info(f"Given token information: {config['general']['token']}")
client_wrapper.set_token_path(config["general"]["token"])
Expand Down
16 changes: 16 additions & 0 deletions dbc2val/dbcfeederlib/clientwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def __init__(self, ip: str, port: int, token_path: str, tls: bool = True):
self._token_path = token_path
self._tls = tls
self._registered = False
self._root_ca_path = None
self._tls_server_name = None

def set_ip(self, ip: str):
""" Set IP address to use """
Expand All @@ -52,6 +54,20 @@ def set_tls(self, tls: bool):
"""
self._tls = tls

def get_tls(self) -> bool:
"""
Return TLS setting
"""
return self._tls

def set_root_ca_path(self, path: str):
""" Set Path for Root CA (CA.pem) """
self._root_ca_path = path

def set_tls_server_name(self, name: str):
""" Set Path for Root CA (CA.pem) """
self._tls_server_name = name

def set_token_path(self, token_path: str):
self._token_path = token_path

Expand Down
22 changes: 20 additions & 2 deletions dbc2val/dbcfeederlib/databrokerclientwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import contextlib

import grpc.aio
from pathlib import Path

import kuksa_client.grpc
from kuksa_client.grpc import Datapoint
Expand Down Expand Up @@ -90,10 +91,19 @@ def start(self):
# The alternative approach would be to provide token in constructor
# with/without ensure_startup_connection and not actively call "authorize"
# The only practical difference is how progress and errors (if any) are reported!

# If there is a path VSSClient will request a secure connection
if self._tls and self._root_ca_path:
root_path = Path(self._root_ca_path)
else:
root_path = None

self._grpc_client = self._exit_stack.enter_context(kuksa_client.grpc.VSSClient(
host=self._ip,
port=self._port,
ensure_startup_connection=False
ensure_startup_connection=False,
root_certificates=root_path,
tls_server_name=self._tls_server_name
))
self._grpc_client.authorize(token=self._token, **self._rpc_kwargs)
self._grpc_client.channel.subscribe(
Expand Down Expand Up @@ -198,7 +208,15 @@ async def subscribe(self, vss_names: List[str], callback):
subscribe_entry = SubscribeEntry(name, View.FIELDS, [Field.ACTUATOR_TARGET])
log.info(f"Subscribe entry: {subscribe_entry}")
entries.append(subscribe_entry)
async with VSSClient(self._ip, self._port, token=self._token) as client:

# If there is a path VSSClient will request a secure connection
if self._tls and self._root_ca_path:
root_path = Path(self._root_ca_path)
else:
root_path = None

async with VSSClient(self._ip, self._port, token=self._token,
root_certificates=root_path, tls_server_name=self._tls_server_name) as client:
async for updates in client.subscribe(entries=entries):
log.debug(f"Received update of length {len(updates)}")
await callback(updates)
5 changes: 4 additions & 1 deletion dbc2val/dbcfeederlib/serverclientwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ def start(self):
if self._token_path != "":
self._client_config["token"] = self._token_path

# TODO add data for root cert if using TLS and if given
if self._root_ca_path:
self._client_config['cacertificate'] = self._root_ca_path
if self._tls_server_name:
self._client_config['tls_server_name'] = self._tls_server_name

self._kuksa = KuksaClientThread(self._client_config)
self._kuksa.start()
Expand Down
36 changes: 17 additions & 19 deletions dbc2val/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#
# This file is autogenerated by pip-compile with Python 3.8
# This file is autogenerated by pip-compile with Python 3.10
# by the following command:
#
# pip-compile requirements.in
# pip-compile --pre requirements.in
#
argparse-addons==0.12.0
# via cantools
Expand All @@ -22,47 +22,45 @@ crccheck==1.3.0
# via cantools
decorator==5.1.1
# via jsonpath-ng
diskcache==5.5.1
diskcache==5.6.1
# via cantools
exceptiongroup==1.1.1
# via pytest
grpcio==1.53.0
# via
# grpcio-tools
# kuksa-client
grpcio-tools==1.53.0
grpcio==1.56.0rc2
# via grpcio-tools
grpcio-tools==1.56.0rc2
# via kuksa-client
iniconfig==2.0.0
# via pytest
jsonpath-ng==1.5.3
# via kuksa-client
kuksa-client==0.4.0a1
kuksa-client==0.4.0a3
# via -r requirements.in
msgpack==1.0.5
# via python-can
numpy==1.24.2
numpy==1.25.0
# via can-j1939
packaging==23.1
# via
# pytest
# python-can
pluggy==1.0.0
pluggy==1.2.0
# via pytest
ply==3.11
# via jsonpath-ng
protobuf==4.22.3
protobuf==4.23.3
# via grpcio-tools
py-expression-eval==0.3.14
# via -r requirements.in
pygments==2.15.0
pygments==2.15.1
# via kuksa-client
pyperclip==1.8.2
# via cmd2
pyserial==3.5
# via -r requirements.in
pytest==7.3.1
pytest==7.3.2
# via can-j1939
python-can==4.1.0
python-can==4.2.2
# via
# -r requirements.in
# can-j1939
Expand All @@ -75,17 +73,17 @@ textparser==0.24.0
# via cantools
tomli==2.0.1
# via pytest
types-protobuf==4.22.0.2
types-protobuf==4.23.0.1
# via -r requirements.in
types-pyyaml==6.0.12.9
types-pyyaml==6.0.12.10
# via -r requirements.in
typing-extensions==4.5.0
typing-extensions==4.7.0rc1
# via
# cantools
# python-can
wcwidth==0.2.6
# via cmd2
websockets==11.0.1
websockets==11.0.3
# via kuksa-client
wrapt==1.15.0
# via python-can
Expand Down
6 changes: 4 additions & 2 deletions dds2val/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The DDS provider provides data from an DDS middleware/API. For further understan

### KML replay

*This requires that you already haev created the virtual environment as described in "local build" above*
*This requires that you already have created the virtual environment as described in "local build" above*

1. `source env/bin/activate`
2. `pip install -r requirements/requirements-kml.txt`
Expand All @@ -38,12 +38,14 @@ Configuration for the DDS provider is solved through setting environment variabl

| Environment variable | default value | description |
| ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| VEHICLEDATABROKER_DAPR_APP_ID | None | DAPR ID for Vehicle App to look for. For more information to Vehicle Apps visit [Velocitas](https://eclipse-velocitas.github.io/velocitas-docs/) |
| VDB_ADDRESS | 127.0.0.1 | Address where to look for (vehicle) databroker |
| VEHICLEDATABROKER_DAPR_APP_ID | None | DAPR ID for Vehicle App to look for. For more information to Vehicle Apps visit [Velocitas](https://eclipse-velocitas.github.io/velocitas-docs/) |
| DAPR_GRPC_PORT | None | If [DAPR](https://dapr.io/) gets used port of DAPR Sidecar. Overwrites VDB_PORT variable |
| MAPPING_FILE | mapping/latest/mapping.yml | Place of mapping file from DDS to VSS |
| VDB_PORT | 55555 | On which port the (vehicle) databroker is expected. If you want to use DAPR use DAPR_GRPC_PORT. |
| TOKEN | None | JWT token which will get used to authorize to databroker; You can set on linux through `export TOKEN=$(< PATH_TO_kuksa.val/jwt/provide-all.token)` or `export TOKEN=<content of a file>`| |
| VDB_ROOT_CA_PATH | None | Path to root CA. If defined the client will attempt to use a secure connection and identify the server using this certificate. |
| VDB_TLS_SERVER_NAME | None | TLS server name, may be needed if addressing a server by IP-name |

## Overall sequence

Expand Down
16 changes: 14 additions & 2 deletions dds2val/ddsprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,25 @@ async def main():
port = os.environ.get("DAPR_GRPC_PORT")
else:
port = os.environ.get("VDB_PORT", "55555")
databroker_address = os.environ.get("VDB_ADDRESS", "127.0.0.1:") + port
databroker_address = os.environ.get("VDB_ADDRESS", "127.0.0.1") + ":" + port

mappingfile = os.environ.get(
"MAPPING_FILE", str(Path(__file__).parent / "mapping/latest/mapping.yml")
)

ddsprovider = helper.Ddsprovider()
# Collect data for TLS connection, for now default is no TLS
# To keep backward compatibility not using TLS is default
if os.environ.get("VDB_ROOT_CA_PATH"):
root_ca_path = os.environ.get("VDB_ROOT_CA_PATH")
else:
root_ca_path = None

if os.environ.get("VDB_TLS_SERVER_NAME"):
tls_server_name = os.environ.get("VDB_TLS_SERVER_NAME")
else:
tls_server_name = None

ddsprovider = helper.Ddsprovider(root_ca_path, tls_server_name)

# Handler for Ctrl-C and Kill signal
def signal_handler(signal_received, _frame):
Expand Down
16 changes: 14 additions & 2 deletions dds2val/ddsproviderlib/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import contextlib
import logging
import time
from typing import Optional

# pylint: disable=unused-import
import sensor_msgs # # noqa: F401
Expand All @@ -24,6 +25,8 @@
# pylint: disable=unused-import
import std_msgs # # noqa: F401

from pathlib import Path

# F401 Vehicle module is used inside the eval block to access the data classes
# pylint: disable=unused-import
import Vehicle # noqa: F401
Expand Down Expand Up @@ -96,7 +99,7 @@ class Ddsprovider:
"""class to work with DDS feeder."""

# pylint: disable=too-many-instance-attributes
def __init__(self):
def __init__(self, root_ca_path: Optional[str] = None, tls_server_name: Optional[str] = None):
self._shutdown = False
self._provider = None
self._registered = False
Expand All @@ -105,6 +108,8 @@ def __init__(self):
self._reader = []
self._listener = None
self._mapper = None
self._root_ca_path = root_ca_path
self._tls_server_name = tls_server_name
self._exit_stack = contextlib.ExitStack()

async def start(self, databroker_address, grpc_metadata, mappingfile, token):
Expand All @@ -118,9 +123,16 @@ async def start(self, databroker_address, grpc_metadata, mappingfile, token):
self._mapper = vss2ddsmapper.Vss2DdsMapper(mappingfile)
host, port = databroker_address.split(":")

# If there is a path VSSClient will request a secure connection
if self._root_ca_path:
root_path = Path(self._root_ca_path)
else:
root_path = None

try:
vss_client = self._exit_stack.enter_context(
VSSClient(host=host, port=port, token=token)
VSSClient(host=host, port=port, token=token,
root_certificates=root_path, tls_server_name=self._tls_server_name)
)
except VSSClientError as kuksa_error:
log.error(kuksa_error)
Expand Down
1 change: 0 additions & 1 deletion gps2val/config/gpsd_feeder.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# insecure = False / True
# Paths to files that are needed: for default files see https://github.com/boschglobal/kuksa.val/tree/master/kuksa_certificates
# token_or_tokenfile = your token or path to a file storing the token to be used for authentication
# certificate = your path to Client.pem
# cacertificate = your path to CA.pem
# key = your path to Client.key

Expand Down
Loading

0 comments on commit e0793a9

Please sign in to comment.