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

Commit

Permalink
DBC and GPS Feeder TLS support
Browse files Browse the repository at this point in the history
  • Loading branch information
erikbosch committed Jun 22, 2023
1 parent e484416 commit a38bc52
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 53 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
12 changes: 11 additions & 1 deletion 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
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
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
20 changes: 10 additions & 10 deletions gps2val/gpsd_feeder.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ def shutdown(self):
logging.getLogger(logger).setLevel(level)

manual_config = argparse.ArgumentParser()
manual_config.add_argument("--host",
manual_config.add_argument("--ip",
help="Specify the host where too look for KUKSA.val server/databroker; "
"default: 127.0.0.1",
nargs='?', default="127.0.0.1")
Expand All @@ -261,16 +261,16 @@ def shutdown(self):
"If you want to connect to KUKSA.val databroker specify grpc; default: ws",
nargs='?', default="ws")
manual_config.add_argument("--insecure",
help="For KUKSA.val server specify False, "
"for KUKSA.val databroker there is currently no security so specify True; "
"default: False",
help="Specify if you want an insecure connection (i.e. without TLS)"
"default: False",
nargs='?', default="False")
manual_config.add_argument("--certificate",
help="Specify the path to your Client.pem file; default: Client.pem",
nargs='?', default="Client.pem")
manual_config.add_argument("--cacertificate",
help="Specify the path to your CA.pem; default: CA.pem",
help="Specify the path to your CA.pem; default: CA.pem. "
"Needed when not using insecure mode",
nargs='?', default="CA.pem")
manual_config.add_argument("--tls-server-name",
help="TLS server name, may be needed if addressing a server by IP-name",
nargs='?', default="")
manual_config.add_argument("--token",
help="Specify the JWT token string or the path to your JWT token; default: "
"authentication information not specified",
Expand All @@ -293,12 +293,12 @@ def shutdown(self):
config_object = configparser.ConfigParser()
log.info("No configuration file found. Using default values.")
config_object["kuksa_val"] = {
"host": args.host,
"ip": args.ip,
"port": args.port,
"protocol": args.protocol,
"insecure": args.insecure,
"certificate": args.certificate,
"cacertificate": args.cacertificate,
"tls_server_name": args.tls_server_name,
"token_or_tokenfile": args.token,
"file": args.file,
}
Expand Down
57 changes: 37 additions & 20 deletions gps2val/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,32 @@ If you do not have a gps device, you can use your cellphone to forward gps data
gpsd -N udp://0.0.0.0:29998
```
## Install dependencies and execution
usage: gpsd_feeder.py [-h] [--host [HOST]] [--port [PORT]] [--protocol [PROTOCOL]] [--insecure [INSECURE]] [--certificate [CERTIFICATE]] [--cacertificate [CACERTIFICATE]] [--token [TOKEN]]

```
usage: gpsd_feeder.py [-h] [--ip [IP]] [--port [PORT]] [--protocol [PROTOCOL]] [--insecure [INSECURE]] [--cacertificate [CACERTIFICATE]] [--tls-server-name [TLS_SERVER_NAME]] [--token [TOKEN]]
[--file [FILE]] [--gpsd_host [GPSD_HOST]] [--gpsd_port [GPSD_PORT]] [--interval [INTERVAL]]
options:
>-h, --help show this help message and exit
>--host [HOST] Specify the host where too look for KUKSA.val server/databroker; default: 127.0.0.1
>--port [PORT] Specify the port where too look for KUKSA.val server/databroker; default: 8090
>--protocol [PROTOCOL]
If you want to connect to KUKSA.val server specify ws. If you want to connect to KUKSA.val databroker specify grpc; default: ws
>--insecure [INSECURE]
For KUKSA.val server specify False, for KUKSA.val databroker there is currently no security so specify True; default: False
>--certificate [CERTIFICATE]
Specify the path to your Client.pem file; default: Client.pem
>--cacertificate [CACERTIFICATE]
Specify the path to your CA.pem; default: CA.pem
>--token [TOKEN] Specify the JWT token or the path to your JWT token; default: token information not specified
>--file [FILE] Specify the path to your config file; default: not specifed
>--gpsd_host [GPSD_HOST]
Specify the host for gpsd to start on; default: 127.0.0.1
>--gpsd_port [GPSD_PORT]
Specify the port for gpsd to start on; default: 2948
>--interval [INTERVAL]
Specify the interval time for feeding gps data; default: 1
-h, --help show this help message and exit
--ip [IP] Specify the host where too look for KUKSA.val server/databroker; default: 127.0.0.1
--port [PORT] Specify the port where too look for KUKSA.val server/databroker; default: 8090
--protocol [PROTOCOL]
If you want to connect to KUKSA.val server specify ws. If you want to connect to KUKSA.val databroker specify grpc; default: ws
--insecure [INSECURE]
Specify if you want an insecure connection (i.e. without TLS)default: False
--cacertificate [CACERTIFICATE]
Specify the path to your CA.pem; default: CA.pem. Needed when not using insecure mode
--tls-server-name [TLS_SERVER_NAME]
TLS server name, may be needed if addressing a server by IP-name
--token [TOKEN] Specify the JWT token string or the path to your JWT token; default: authentication information not specified
--file [FILE] Specify the path to your config file; by default not defined
--gpsd_host [GPSD_HOST]
Specify the host for gpsd to start on; default: 127.0.0.1
--gpsd_port [GPSD_PORT]
Specify the port for gpsd to start on; default: 2948
--interval [INTERVAL]
Specify the interval time for feeding gps data; default: 1
```

A template config file that can be used together with the `--file` option
exists in [config/gpsd_feeder.ini](config/gpsd_feeder.ini). Note that if `--file` is specified all other options
Expand All @@ -52,10 +55,24 @@ gpsd_feeder will try to authenticate itself towards the KUKSA.val Server/Databro
Note that the KUKSA.val Databroker by default does not require authentication.

An example for authorizing against KUKSA.val Databroker using an example token is shown below.

```
python gpsd_feeder.py --protocol grpc --port 55555 --insecure true --token /home/user/kuksa.val/jwt/provide-all.token
```

## TLS

The KUKSA.val GPS Feeder supports using TLS connections. A TLS connection will be used unless `--insecure=True`
is specified. When using a TLS connection a path to the root certificate used by the Server/Databroker must be given.
The client validates the name of the server against the certificate provided by the Server/Databroker.
If addressing with a numeric IP-address and using grpc as protocol the "real" server name must be given using
`--tls-server-name`. For the [KUKSA.val example certificates](https://github.com/eclipse/kuksa.val/tree/master/kuksa_certificates)
the names `localhost`and `Server`can be used.

```
python gpsd_feeder.py --port 55555 --protocol grpc --ip 127.0.0.1 --cacertificate ~/kuksa.val/kuksa_certificates/CA.pem --tls-server-name Server
```

### Using docker
You can also use `docker` to execute the feeder platform independently.
To build a docker image:
Expand Down

0 comments on commit a38bc52

Please sign in to comment.