diff --git a/.github/workflows/kuksa-client.yml b/.github/workflows/kuksa-client.yml index 9b5d3391e..66a5c2554 100644 --- a/.github/workflows/kuksa-client.yml +++ b/.github/workflows/kuksa-client.yml @@ -23,7 +23,6 @@ on: - "kuksa-client/**" workflow_dispatch: - jobs: checkrights: uses: ./.github/workflows/check_push_rights.yml @@ -95,3 +94,28 @@ jobs: push: false tags: "ttl.sh/kuksa.val/kuksa-client-${{github.sha}}:1h" labels: ${{ steps.meta.outputs.labels }} + + + dbc2val-test: + runs-on: ubuntu-latest + steps: + - name: Checkout kuksa.val + uses: actions/checkout@v3 + - name: Install pip + run: | + python -m pip --quiet --no-input install --upgrade pip + - name: Install dependencies with pip + run: | + cd kuksa-client + pip install -r requirements.txt -e . + pip install -r test-requirements.txt + - name: Run tests + run: | + cd kuksa-client + pytest + - name: Build pypi package + run: | + cd kuksa-client + pip install --upgrade build + python -m build + diff --git a/Cargo.lock b/Cargo.lock index a38c70d08..71b8ebf9f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2343,14 +2343,14 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f" dependencies = [ "log", "ring", + "rustls-webpki", "sct", - "webpki", ] [[package]] @@ -2362,6 +2362,16 @@ dependencies = [ "base64 0.21.2", ] +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.12" @@ -2807,13 +2817,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ "rustls", "tokio", - "webpki", ] [[package]] @@ -2844,14 +2853,14 @@ dependencies = [ [[package]] name = "tonic" -version = "0.8.3" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +checksum = "3082666a3a6433f7f511c7192923fa1fe07c69332d3c6a2e6bb040b569199d5a" dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.13.1", + "base64 0.21.2", "bytes", "futures-core", "futures-util", @@ -2863,17 +2872,14 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "prost-derive", "rustls-pemfile", "tokio", "tokio-rustls", "tokio-stream", - "tokio-util", "tower", "tower-layer", "tower-service", "tracing", - "tracing-futures", ] [[package]] @@ -2953,16 +2959,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-subscriber" version = "0.3.17" @@ -3182,16 +3178,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "which" version = "4.4.0" diff --git a/Cargo.toml b/Cargo.toml index 9e5af7768..51db3f1e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ prost-types = "0.11" tokio = "1.17.0" # tokio-stream has no features tokio-stream = "0.1.8" -tonic = { version = "0.8", default-features = false } +tonic = { version = "0.9.1", default-features = false } tonic-build = { version = "0.8", default-features = false } [profile.release] diff --git a/README.md b/README.md index c481cf612..5ab6f08d9 100644 --- a/README.md +++ b/README.md @@ -28,5 +28,7 @@ KUKSA.val contains several components | [Examples](./kuksa_apps) | Multiple example apps for different programming languages and frameworks | [Feeders](https://github.com/eclipse/kuksa.val.feeders/) | Multiple feeders to gathering vehicle data and transforming it to VSS suitable for kuksa-val-server +## More information +* [KUKSA.val TLS Concept](doc/tls.md) diff --git a/doc/quickstart.md b/doc/quickstart.md index 0bf32ccd0..3e82860c7 100644 --- a/doc/quickstart.md +++ b/doc/quickstart.md @@ -2,11 +2,13 @@ The quickest possible way to get KUKSA.val up and running +*Note: The examples in this document do not use TLS or access control.* + ## Starting broker First we want to run KUKSA.val databroker ``` -docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker:master +docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker:master --insecure ``` @@ -16,7 +18,7 @@ You can interact with the VSS datapoints using the cli clients. The first option This is, how you start it: ``` -docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker-cli:master +docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker-cli:master ``` Here is how you can use it: @@ -91,7 +93,10 @@ print("Finished.") ``` Do a `pip install kuksa-client` and start with + +``` python ./speed_provider.py +``` ### Subscribing data: Create a file `speed_subscriber.py` with the following content @@ -109,26 +114,29 @@ with VSSClient('127.0.0.1', 55555) as client: ``` Do a `pip install kuksa-client` and start with -python ./speed_subscriber.py +``` +python ./speed_subscriber.py +``` ## FAQ & Notes -Frequently anticipated questions and tipps. +Frequently anticipated questions and tips. ### This is not working on OS X -Unfortunately OS X has a bug that does not allow you to use the databroker default 55555. To change when starting the server: +Unfortunately OS X has a bug that does not allow you to use the Databroker default port 55555. To change when starting the server: ``` -docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker:master --port 55556 +docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/databroker:master --port 55556 --insecure ``` Using the databroker-cli ``` -docker run -it --rm --net=host -e KUKSA_DATA_BROKER_PORT=55556 ghcr.io/eclipse/kuksa.val/databroker-cli:master +docker run -it --rm --net=host -e KUKSA_DATA_BROKER_PORT=55556 ghcr.io/eclipse/kuksa.val/databroker-cli:master ``` Using kuksa-client CLI + ``` docker run -it --rm --net=host ghcr.io/eclipse/kuksa.val/kuksa-client:master --port 55556 --protocol grpc --insecure ``` @@ -143,7 +151,7 @@ One alternative is using a Docker distribution, that does support it even on Mac With Docker Desktop you can still forward ports, so this should work: ``` -docker run -it --rm --publish 55556:55556 ghcr.io/eclipse/kuksa.val/databroker:master --port 55556 +docker run -it --rm --publish 55556:55556 ghcr.io/eclipse/kuksa.val/databroker:master --port 55556 --insecure ``` From your host computer you can now reach databroker at `127.0.0.1:55556`. To connect from another container, you need to use your computers IP address (**not** 127.0.0.1), i.e. to use the client @@ -162,7 +170,7 @@ docker run -it --rm ghcr.io/eclipse/kuksa.val/databroker-cli:master --server ### feed/set: Why is my data not updated? Some VSS points are "sensors", e.g. Vehicle.Speed. You can read/get Vehicle speed, but we are not expecting to be able to influence it via VSS. -Historically components, that gather the actual vehicle speed from some sensors/busses in a vehicle and providing a VSS representation to kuksa.val have been called `feeders`. Hence, to update the current speed in the Rust-cli, you use +Historically components, that gather the actual vehicle speed from some sensors/busses in a vehicle and providing a VSS representation to kuksa.val have been called `feeders`. Hence, to update the current speed in the Rust-cli, you use ``` feed Vehicle.Speed 200 diff --git a/doc/tls.md b/doc/tls.md new file mode 100644 index 000000000..a4cb64207 --- /dev/null +++ b/doc/tls.md @@ -0,0 +1,111 @@ +# KUKSA.val TLS concept + +This page describes the TLS support in KUKSA.val + +## Security concept + +KUKSA.val supports TLS for connection between KUKSA.val Databroker/Server and clients. + +General design concept in short: + +* KUKSA.val Server and KUKSA.val Databroker by default only accept TLS connection. Insecure connections can be allowed by a configuration setting +* Mutual authentication not supported, i.e. KUKSA.val Server and KUKSA.val Databroker does not authenticate clients +* A set of example certificates and keys exist in the [kuksa_certificates](kuksa_certificates) repository +* The example certificates are used as default by some applications +* The example certificates shall only be used during development and re not suitable for production use +* KUKSA.val does not put any additional requirements on what certificates that are accepted, default settings as defined by OpenSSL and gRPC are typically used + +## Example certificates + +For more information see the [README.md](kuksa_certificates/README.md). + +**NOTE: The example keys and certificates shall not be used in your production environment! ** + +## Examples using example certificates + +This section intends to give guidelines on how you can verify TLS functionality with KUKSA.val. +It is based on using the example certificates. + + +## KUKSA.val databroker + +KUKSA.val Databroker supports TLS, but not mutual authentication. +You cannot start KUKSA.val Databroker just using default arguments as you either must specify that insecure connections +shall be used (`--insecure`) or provide data for a secure connection. +To use a secure connection specify `--tls-cert`and `--tls-private-key` + +``` +~/kuksa.val/kuksa_databroker$ cargo run --bin databroker -- --metadata ../data/vss-core/vss_release_4.0.json --tls-cert ../kuksa_certificates/Server.pem --tls-private-key ../kuksa_certificates/Server.key +``` + +Default certificates and keys are not included in the default KUKSA.val Databroker container, +so if running KUKSA.val Databroker from a default container you need to mount the directory containing the keys and certificates. + +``` +~/kuksa.val/kuksa_databroker$ docker run --rm -it -p 55555:55555/tcp -v /home/user/kuksa.val/kuksa_certificates:/certs databroker --tls-cert /certs/Server.pem --tls-private-key /certs/Server.key +``` + +## KUKSA.val databroker-cli + +Can be run in TLS mode like below. +Note that [databroker-cli](kuksa_databroker/databroker-cli/src/main.rs) currently expects the certificate +to have "Server" as subjectAltName. + +``` +~/kuksa.val/kuksa_databroker$ cargo run --bin databroker-cli -- --ca-cert ../kuksa_certificates/CA.pem +``` + +Default certificates and keys are not included in the default KUKSA.val Databroker-cli container, +so if running KUKSA.val Databroker-cli from a default container you need to mount the directory containing the keys and certificates. + +``` +docker run --rm -it --net=host -v /home/user/kuksa.val/kuksa_certificates:/certs databroker-cli --ca-cert /certs/CA.pem +``` + +## KUKSA.val Server + +Uses TLS by default, but doe not support mutual TLS. By default it uses KUKSA.val example certificates/keys `Server.key`, `Server.pem` and `CA.pem`. + +``` +~/kuksa.val/kuksa-val-server/build/src$ ./kuksa-val-server --vss ./vss_release_4.0.json +``` + +It is posible to specify a different certificate path, but the file names must be the same as listed above. + +``` +~/kuksa.val/kuksa-val-server/build/src$ ./kuksa-val-server --vss ./vss_release_4.0.json -cert-path ../../../kuksa_certificates +``` + +In KUKSA.val Server the default certificates and keys are included in the container, so no need to +mount a directory if you want to use default certificates and keys. + +``` +docker run -it --rm -p 127.0.0.1:8090:8090 -e LOG_LEVEL=ALL kuksa-val:latest +``` + +If using the default KUKSA.val Server Docker container there is no mechanism to use different certificates, +as the [Dockerfile](../kuksa-val-server/docker/Dockerfile) specifies that the default shall be used. + +## KUKSA.val Client (command line) + +See [KUKSA.val Client Documentation](../kuksa-client/README.md). + +## KUKSA.val Client (library) + +Clients like [KUKSA.val CAN Feeder](https://github.com/eclipse/kuksa.val.feeders/tree/main/dbc2val) +that use KUKSA.val Client library must typically set the path to the root CA certificate. +If the path is set the VSSClient will try to establish a secure connection. + +``` +# Shall TLS be used (default False for Databroker, True for KUKSA.val Server) +# 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=Server +``` diff --git a/kuksa-client/README.md b/kuksa-client/README.md index e90e81166..be83acd4f 100644 --- a/kuksa-client/README.md +++ b/kuksa-client/README.md @@ -31,9 +31,8 @@ With default CLI arguments, the client will try to connect to a local VISS serve If you wish to connect to a gRPC server e.g. `kuksa-databroker`, you should instead run: ```console -$ kuksa-client --ip 127.0.0.1 --port 55555 --protocol grpc --insecure +$ kuksa-client --ip 127.0.0.1 --port 55555 --protocol grpc ``` -Note: `--insecure` is required because `kuksa-databroker` does not yet support TLS encryption or authentication. If everything works as expected and the server can be contacted you will get an output similar to below. @@ -64,6 +63,55 @@ Test Client> If the connected KUKSA.val Server or KUKSA.val Databroker require authorization the next step is to authorize. KUKSA.val Server and KUKSA.val Databroker use different token formats. +### Connecting to KUKSA.val Databroker + +A gRPC connection to KUKSA.val Databroker is started by specifying address and port for the Databroker and giving +`--protocol grpc` as argument. +KUKSA.val Client use TLS by default, it only run in insecure mode if `--insecure` is given as argument. +By default the KUKSA.val example Root CA and Client keys are used, but client keys have no effect as mutual authentication is not supported by KUKSA.val Databroker or KUKSA.val Server. + +``` +~/kuksa.val/kuksa-client$ kuksa-client --ip localhost --port 55555 --protocol grpc +``` + +This call with all parameters specified give same effect: + +``` +~/kuksa.val/kuksa-client$ kuksa-client --ip localhost --port 55555 --protocol grpc --certificate ../kuksa_certificates/Client.pem --keyfile ../kuksa_certificates/Client.key --cacertificate ./kuksa_certificates/CA.pem +``` + +There is actually no reason to specify client key and certificate, as mutual authentication is not supported in KUKSA.val Databroker, +so the command can be simplified like this: + + +``` +~/kuksa.val/kuksa-client$ kuksa-client --ip localhost --port 55555 --protocol grpc --cacertificate ./kuksa_certificates/CA.pem +``` + +The example server protocol list 127.0.0.1 as an alternative name, but the TLS-client currently used does not accept it, +instead a valid server name must be given as argument. +Currently `Server` and `localhost`are valid names from the example certificates. + +``` +~/kuksa.val/kuksa-client$ kuksa-client --ip 127.0.0.1 --port 55555 --protocol grpc --cacertificate ../kuksa_certificates/CA.pem --tls-server-name Server +``` + +### Connecting to KUKSA.val Server + +Connecting to KUKSA.val Server is default, and TLS is used by default by KUKSA.val Server. +`--tls-server-name` does not need to be used when connecting to KUKSA.val Server, +that is the only difference compared to connecting to KUKSA.val Databroker. + +``` +~/kuksa.val/kuksa-client$ kuksa-client +``` + +This corresponds to this call: + +``` +kuksa-client --ip 127.0.0.1 --port 8090 --protocol ws --cacertificate ./kuksa_certificates/CA.pem +``` + ### Authorizing against KUKSA.val Server The jwt tokens for testing can either be found under [../kuksa_certificates/jwt](../kuksa_certificates/jwt) @@ -82,13 +130,12 @@ Test Client> authorize /some/path/kuksa_certificates/jwt/super-admin.json.token If connecting to Databroker the command `printTokenDir` is not much help as it shows the default token directories for KUKSA.val Server example tokens. If the KUKSA.val Databroker use default example tokens then one of the -tokens in [../jwt](..//jwt) can be used, like in the example below: +tokens in [../jwt](../jwt) can be used, like in the example below: ```console -Test Client> authorize /some/path//jwt/provide-all.token +Test Client> authorize /some/path/jwt/provide-all.token ``` - ## Usage Instructions Refer help for further information @@ -251,6 +298,26 @@ This package holds different APIs depending on your application's requirements. For more information, see ([Documentation](https://github.com/eclipse/kuksa.val/blob/master/kuksa-client/docs/main.md)). +### TLS configuration + +Clients like [KUKSA.val CAN Feeder](https://github.com/eclipse/kuksa.val.feeders/tree/main/dbc2val) +that use KUKSA.val Client library must typically set the path to the root CA certificate. +If the path is set the VSSClient will try to establish a secure connection. + +``` +# Shall TLS be used (default False for Databroker, True for KUKSA.val Server) +# 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=Server +``` + ## Troubleshooting 1. The server/data broker is listening on its port but my client is unable to connect to it and returns an error: diff --git a/kuksa-client/kuksa_client/__main__.py b/kuksa-client/kuksa_client/__main__.py index a580273e3..c301cc43a 100755 --- a/kuksa-client/kuksa_client/__main__.py +++ b/kuksa-client/kuksa_client/__main__.py @@ -21,6 +21,7 @@ import functools import json import logging.config +import logging import os import pathlib import sys @@ -48,6 +49,9 @@ scriptDir = os.path.dirname(os.path.realpath(__file__)) +logger = logging.getLogger(__name__) + + def assignment_statement(arg): path, value = arg.split('=', maxsplit=1) return (path, value) @@ -247,8 +251,10 @@ def subscriptionIdCompleter(self, text, line, begidx, endidx): ap_updateVSSTree.add_argument( "Json", help="Json tree to update VSS", completer_method=jsonfile_completer_method) - # Constructor - def __init__(self, server_ip=None, server_port=None, server_protocol=None, insecure=False, token_or_tokenfile=None): + # Constructor, request names after protocol to avoid errors + def __init__(self, server_ip=None, server_port=None, server_protocol=None, *, insecure=False, token_or_tokenfile=None, + certificate=None, keyfile=None, + cacertificate=None, tls_server_name=None): super().__init__( persistent_history_file=".vssclient_history", persistent_history_length=100, allow_cli_args=False, ) @@ -265,6 +271,10 @@ def __init__(self, server_ip=None, server_port=None, server_protocol=None, insec self.commThread = None self.token_or_tokenfile = token_or_tokenfile self.insecure = insecure + self.certificate = certificate + self.keyfile = keyfile + self.cacertificate = cacertificate + self.tls_server_name = tls_server_name print("Welcome to Kuksa Client version " + str(_metadata.__version__)) print() @@ -485,9 +495,21 @@ def connect(self): config = {'ip': self.serverIP, 'port': self.serverPort, 'insecure': self.insecure, - 'protocol': self.serverProtocol, - 'token_or_tokenfile': self.token_or_tokenfile, + 'protocol': self.serverProtocol } + + # Configs should only be added if they actually have a value + if self.token_or_tokenfile: + config['token_or_tokenfile'] = self.token_or_tokenfile + if self.certificate: + config['certificate'] = self.certificate + if self.keyfile: + config['keyfile'] = self.keyfile + if self.cacertificate: + config['cacertificate'] = self.cacertificate + if self.tls_server_name: + config['tls_server_name'] = self.tls_server_name + self.commThread = KuksaClientThread(config) self.commThread.start() @@ -589,11 +611,34 @@ def main(): parser.add_argument( '--token_or_tokenfile', default=None, help="JWT token or path to a JWT token file (.token)", ) + + # Add TLS arguments + # Note: Databroker does not yet support mutual authentication, so no need to use two first arguments + parser.add_argument( + '--certificate', default=None, help="Client cert file(.pem), only needed for mutual authentication", + ) + parser.add_argument( + '--keyfile', default=None, help="Client private key file (.key), only needed for mutual authentication", + ) + parser.add_argument( + '--cacertificate', default=None, help="Client root cert file (.pem), needed unless insecure mode used", + ) + # Observations for Python + # Connecting to "localhost" works well, subjectAltName seems to suffice + # Connecting to "127.0.0.1" does not work unless server-name specified + # For KUKSA.val example certs default name is "Server" + parser.add_argument( + '--tls-server-name', default=None, help="CA name of server, needed in some cases where subjectAltName does not suffice", + ) + args = parser.parse_args() logging.config.fileConfig(args.logging_config) + clientApp = TestClient(args.ip, args.port, args.protocol, - args.insecure, args.token_or_tokenfile) + insecure = args.insecure, token_or_tokenfile = args.token_or_tokenfile, + certificate = args.certificate, keyfile = args.keyfile, + cacertificate = args.cacertificate, tls_server_name = args.tls_server_name) try: # We exit the loop when the user types "quit" or hits Ctrl-D. clientApp.cmdloop() diff --git a/kuksa-client/kuksa_client/cli_backend/__init__.py b/kuksa-client/kuksa_client/cli_backend/__init__.py index d282a0cfa..f757cd686 100644 --- a/kuksa-client/kuksa_client/cli_backend/__init__.py +++ b/kuksa-client/kuksa_client/cli_backend/__init__.py @@ -21,6 +21,7 @@ import kuksa_certificates + class Backend: def __init__(self, config): self.serverIP = config.get('ip', "127.0.0.1") @@ -34,8 +35,9 @@ def __init__(self, config): 'cacertificate', str(self.default_cert_path / 'CA.pem')) self.certificate = config.get('certificate', str( self.default_cert_path / 'Client.pem')) - self.keyfile = config.get('key', str( + self.keyfile = config.get('keyfile', str( self.default_cert_path / 'Client.key')) + self.tls_server_name = config.get('tls_server_name', "") self.token_or_tokenfile = config.get('token_or_tokenfile', str( self.default_cert_path / 'jwt/all-read-write.json.token')) diff --git a/kuksa-client/kuksa_client/cli_backend/grpc.py b/kuksa-client/kuksa_client/cli_backend/grpc.py index 8e6c6cea2..0c177de05 100644 --- a/kuksa-client/kuksa_client/cli_backend/grpc.py +++ b/kuksa-client/kuksa_client/cli_backend/grpc.py @@ -270,6 +270,7 @@ async def mainLoop(self): root_certificates=self.cacertificate, private_key=self.keyfile, certificate_chain=self.certificate, + tls_server_name=self.tls_server_name, token=self.token ) as vss_client: print("Secure gRPC channel connected.") diff --git a/kuksa-client/kuksa_client/cli_backend/ws.py b/kuksa-client/kuksa_client/cli_backend/ws.py index ad02d7737..260a3667e 100644 --- a/kuksa-client/kuksa_client/cli_backend/ws.py +++ b/kuksa-client/kuksa_client/cli_backend/ws.py @@ -31,7 +31,6 @@ from kuksa_client import cli_backend - class Backend(cli_backend.Backend): def __init__(self, config): super().__init__(config) @@ -235,12 +234,20 @@ async def connect(self): context.load_cert_chain( certfile=self.certificate, keyfile=self.keyfile) context.load_verify_locations(cafile=self.cacertificate) - # Certificates in ../kuksa_certificates does not contain the IP address used for - # connection to server so hostname check must be disabled - context.check_hostname = False + # We want host name to match + # For example certificates we use subjectAltName to make it match for Server, localahost and 127.0.0.1 + # If using your own certificates make sure that name is included or use tls_server_name work-around + context.check_hostname = True try: print("connect to wss://"+self.serverIP+":"+str(self.serverPort)) - async with websockets.connect("wss://"+self.serverIP+":"+str(self.serverPort), ssl=context) as ws: + args = {'uri':"wss://"+self.serverIP+":"+str(self.serverPort),'ssl':context} + # If your certificates does not contain the name of the server + # (for example during testing, in production it should match) + # then you can specify that checks should be against a different name + if self.tls_server_name: + args['server_hostname'] = self.tls_server_name + + async with websockets.connect(**args) as ws: print("Websocket connected securely.") await self._msgHandler(ws) except OSError as e: diff --git a/kuksa-client/kuksa_client/grpc/__init__.py b/kuksa-client/kuksa_client/grpc/__init__.py index 2dbf6141a..1d5c7ab56 100644 --- a/kuksa-client/kuksa_client/grpc/__init__.py +++ b/kuksa-client/kuksa_client/grpc/__init__.py @@ -40,7 +40,6 @@ from kuksa.val.v1 import val_pb2 from kuksa.val.v1 import val_pb2_grpc - logger = logging.getLogger(__name__) @@ -482,35 +481,47 @@ class ServerInfo: def from_message(cls, message: val_pb2.GetServerInfoResponse): return cls(name=message.name, version=message.version) - class BaseVSSClient: def __init__( self, host: str, port: int, - token: str = None, + token: Optional[str] = None, root_certificates: Optional[Path] = None, private_key: Optional[Path] = None, certificate_chain: Optional[Path] = None, ensure_startup_connection: bool = True, connected: bool = False, + tls_server_name: Optional[str] = None ): + + self.authorization_header = self.get_authorization_header(token) self.target_host = f'{host}:{port}' self.root_certificates = root_certificates self.private_key = private_key self.certificate_chain = certificate_chain + self.tls_server_name = tls_server_name self.ensure_startup_connection = ensure_startup_connection self.connected = connected self.client_stub = None def _load_creds(self) -> Optional[grpc.ChannelCredentials]: - if all((self.root_certificates, self.private_key, self.certificate_chain)): + if self.root_certificates: + logger.info(f"Using TLS with Root CA from {self.root_certificates}") root_certificates = self.root_certificates.read_bytes() - private_key = self.private_key.read_bytes() - certificate_chain = self.certificate_chain.read_bytes() - return grpc.ssl_channel_credentials(root_certificates, private_key, certificate_chain) + if self.private_key and self.certificate_chain: + private_key = self.private_key.read_bytes() + certificate_chain = self.certificate_chain.read_bytes() + logger.info(f"Using private client key {self.private_key} " + f"and chain/certificate {self.certificate_chain}") + return grpc.ssl_channel_credentials(root_certificates, private_key, certificate_chain) + else: + logger.info(f"No client certificates provided, mutual TLS not supported!") + return grpc.ssl_channel_credentials(root_certificates) + logger.info(f"No Root CA present, it will not be posible to use a secure connection!") return None + def _prepare_get_request(self, entries: Iterable[EntryRequest]) -> val_pb2.GetRequest: req = val_pb2.GetRequest(entries=[]) @@ -633,10 +644,20 @@ def connect(self, target_host=None): creds = self._load_creds() if target_host is None: target_host = self.target_host + if creds is not None: - channel = grpc.secure_channel(target_host, creds) + logger.info("Establishing secure channel") + if self.tls_server_name: + logger.info(f"Using TLS server name {self.tls_server_name}") + options = [('grpc.ssl_target_name_override', self.tls_server_name)] + channel = grpc.secure_channel(target_host, creds, options) + else: + logger.debug(f"Not providing explicit TLS server name") + channel = grpc.secure_channel(target_host, creds) else: + logger.info("Establishing insecure channel") channel = grpc.insecure_channel(target_host) + self.channel = self.exit_stack.enter_context(channel) self.client_stub = val_pb2_grpc.VALStub(self.channel) self.connected = True diff --git a/kuksa-client/kuksa_client/grpc/aio.py b/kuksa-client/kuksa_client/grpc/aio.py index ae72136d2..118139740 100644 --- a/kuksa-client/kuksa_client/grpc/aio.py +++ b/kuksa-client/kuksa_client/grpc/aio.py @@ -68,8 +68,16 @@ async def connect(self, target_host=None): if target_host is None: target_host = self.target_host if creds is not None: - channel = grpc.aio.secure_channel(target_host, creds) + logger.info("Establishing secure channel") + if self.tls_server_name: + logger.info(f"Using TLS server name {self.tls_server_name}") + options = [('grpc.ssl_target_name_override', self.tls_server_name)] + channel = grpc.aio.secure_channel(target_host, creds, options) + else: + logger.debug(f"Not providing explicit TLS server name") + channel = grpc.aio.secure_channel(target_host, creds) else: + logger.info("Establishing insecure channel") channel = grpc.aio.insecure_channel(target_host) self.channel = await self.exit_stack.enter_async_context(channel) self.client_stub = val_pb2_grpc.VALStub(self.channel) diff --git a/kuksa-client/requirements.txt b/kuksa-client/requirements.txt index f5e5bdb72..daa4d5ca4 100644 --- a/kuksa-client/requirements.txt +++ b/kuksa-client/requirements.txt @@ -1,5 +1,5 @@ # -# 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 --resolver=backtracking setup.cfg @@ -12,11 +12,9 @@ colorama==0.4.6 # via cmd2 decorator==5.1.1 # via jsonpath-ng -grpcio==1.51.1 - # via - # grpcio-tools - # kuksa-client (setup.cfg) -grpcio-tools==1.51.1 +grpcio==1.54.2 + # via grpcio-tools +grpcio-tools==1.54.2 # via kuksa-client (setup.cfg) jsonpath-ng==1.5.3 # via kuksa-client (setup.cfg) diff --git a/kuksa-client/setup.cfg b/kuksa-client/setup.cfg index bd87a7c49..e4e01f488 100644 --- a/kuksa-client/setup.cfg +++ b/kuksa-client/setup.cfg @@ -28,8 +28,7 @@ install_requires = cmd2 >= 1.4, <2.0 pygments >= 2.5 importlib_metadata >= 3.6 ; python_version < "3.8" - grpcio >= 1.46.0 - grpcio-tools >= 1.46.0 + grpcio-tools >= 1.54.2 jsonpath-ng >= 1.5.3 packages = find: diff --git a/kuksa-client/test-requirements.txt b/kuksa-client/test-requirements.txt index 035f3876e..68fac8ece 100644 --- a/kuksa-client/test-requirements.txt +++ b/kuksa-client/test-requirements.txt @@ -1,5 +1,5 @@ # -# 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 --extra=test --output-file=test-requirements.txt --resolver=backtracking setup.cfg @@ -22,11 +22,9 @@ dill==0.3.6 # via pylint exceptiongroup==1.0.4 # via pytest -grpcio==1.51.1 - # via - # grpcio-tools - # kuksa-client (setup.cfg) -grpcio-tools==1.51.1 +grpcio==1.54.2 + # via grpcio-tools +grpcio-tools==1.54.2 # via kuksa-client (setup.cfg) iniconfig==1.1.1 # via pytest @@ -78,10 +76,6 @@ tomli==2.0.1 # pytest tomlkit==0.11.6 # via pylint -typing-extensions==4.4.0 - # via - # astroid - # pylint wcwidth==0.2.5 # via cmd2 websockets==10.4 diff --git a/kuksa-client/tests/test_grpc.py b/kuksa-client/tests/test_grpc.py index d9c6af135..823e4fea2 100644 --- a/kuksa-client/tests/test_grpc.py +++ b/kuksa-client/tests/test_grpc.py @@ -285,7 +285,7 @@ class TestDatapoint: (DataType.BOOLEAN, (True, datetime.datetime(2022, 11, 16, tzinfo=datetime.timezone.utc)), types_pb2.Datapoint( bool=True, timestamp=timestamp_pb2.Timestamp(seconds=1668556800), )), - (DataType.INT8_ARRAY, ([-128, 127],), types_pb2.Datapoint( + (DataType.INT8_ARRAY, ('[-128, 127]',), types_pb2.Datapoint( int32_array=types_pb2.Int32Array(values=[-128, 127]))), ]) def test_to_message(self, value_type, init_args, message): diff --git a/kuksa_certificates/Client.pem b/kuksa_certificates/Client.pem index d2257364e..ff0e12238 100644 --- a/kuksa_certificates/Client.pem +++ b/kuksa_certificates/Client.pem @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIDxzCCAq+gAwIBAgIUR95d9Q58DithW55HdpMy27QH25owDQYJKoZIhvcNAQEL +MIIEvDCCA6SgAwIBAgIUficQzSD+1RjwvQQBvs3jGdvrkSkwDQYJKoZIhvcNAQEL BQAwgZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZP dHRhd2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTAT BgNVBAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVj -bGlwc2Uub3JnMB4XDTIzMDMyNDEzNDAwMloXDTI0MDMyMzEzNDAwMlowgY4xCzAJ +bGlwc2Uub3JnMB4XDTIzMDYxNjEwMDQxNloXDTI0MDYxNTEwMDQxNlowgY4xCzAJ BgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRhd2ExJTAj BgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xDzANBgNVBAMMBkNs aWVudDEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlwc2Uub3JnMIIBIjAN @@ -13,11 +13,16 @@ Ql8YjnGQjknelBEHW4BUnWY91jV1KaEqmXWmo79nI6KpSP0Bd5xc02YeXBEBF3vw BhhzRzFg851B/pfxnaagsTIgSjXxm1aPloaWmDnqkfLETcgtSCjKRIirRRto1KyK onWXukgK9tDM3Mn4DFYWUmpx/6u03fI2EG0ydNORYeYZRBwlY7SjWLt20Y89TeB9 ylWOTFo+3Pl5o9aLAZQRR/fMLayXAA//NF2AVQjaDbrVcDPn1nh0D8/2WQIDAQAB -oxUwEzARBgNVHREECjAIggZDbGllbnQwDQYJKoZIhvcNAQELBQADggEBAKNyoumi -5sz6SPIERtYijQMoBLLJjiSPI90dlCkAPgVSxNNy0ha1m+AInvgfNKZIpnhIl704 -TBfkj+zSSZC2U4MuHAzSVUIdj0qiitZhb3QNeHvUIYfHk39DRcD8GXrXZYejlprQ -yLo4sjfpiWGbb1AAxO04NBjHYhZHi0MtNkMBSRmnGkSPKi6MUCI14bnyCyyufmaD -I+d32WFC0Hd5jyHuKsEw4pRhmdyU03P8uEd8gfa3D9+0cB7TXLBTulE6D2iN44mk -4QO8uoq/lAzthUkTgOWXHawuiK6kbBi1746J/4gJZztx9jDrGJ3AdHcuo2fH291a -1P2R9vUGGvAoBi0= +o4IBCDCCAQQwIgYDVR0RBBswGYIGQ2xpZW50gglsb2NhbGhvc3SHBH8AAAEwHQYD +VR0OBBYEFA4jHcXnfq57W+So2Z2v8v7eHh9AMIG+BgNVHSMEgbYwgbOhgZqkgZcw +gZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRh +d2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTATBgNV +BAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlw +c2Uub3JnghR92Q2Epkh+nIVseTvM7XpxzB0XwjANBgkqhkiG9w0BAQsFAAOCAQEA +MglEiCcj7XWYT8O21SEbwlPT9BvLtAwfm7a3NqLoWPthJcJZcVPTzuVjMV64OEyQ +PItc6JUZfz7+pH3iGCoE0ghqeyIk6CAQwWdKk6rUq+Sy0sP+jovlSsppel0JbY1K +x0DtQIQWs2ZvjSrMJXbw+PLb4fYz3+rc3VO3C3IZhhRlu9xZ/qqZwzp5jubg1QLb +b3jZg/suRbNfE2lTc4L7YpODRmklg2y9vas1JwMEN3QX9U6oBvTQwClHHU8tU66a +LO98qIDAkZ1ZBjZn+tYcIjC7KbSCcxFlHgWa+7ObJp3IVK2HRnEEBf5XWmixgwKE +XXOU3sQ5ADoNMEEUOzrK1A== -----END CERTIFICATE----- diff --git a/kuksa_certificates/README.md b/kuksa_certificates/README.md index 27fdc0df4..a306200f0 100644 --- a/kuksa_certificates/README.md +++ b/kuksa_certificates/README.md @@ -6,8 +6,25 @@ Many of the clients and servers in this repository use keys, tokens and certific ## Keys and Certificates for TLS connections -The example root certificate is valid for 3650 days ( 10 years). Example client and server certificates are valid for 365 days (1 year). -If they have expired new certificates must be generated as described below. +This directory contain a set of example certificates, used by the KUKSA-project during development and testing. +They may or may not be useful for your test environment. +If needed you can customize `genCerts.sh` and generate keys and certificates that fits your environment. + +See the [KUKSA.val TLS documentation](../tls.md) for general information on the KUKSA.val TLS concept. + +This directory contains the following files with cryptographical information. + + Component | Description | +| -------------- | ----------- | +| `CA.key` | Root key, tnot needed by KUKSA.val applications +| `CA.pem` | Root certificate, valid for 3650 days ( 10 years). | +| `Server.key` | Server key, needed by KUKSA.val Databroker/Server for TLS. | +| `Server.pem` | Server certificate chain, valid for 365 days, needed by KUKSA.val Databroker/Server for TLS. | +| `CLient.key` | Client key, currently not needed as mutual authentication is not supported. | +| `Server.pem` | Client certificate chain, valid for 365 days, currently not needed as mutual authentication is not supported. | + +If the certificates have expired or you by any other reason need to regenerate keys or certificates you can use +the `genCerts.sh` cript as described below. ### Generating Keys and Certificates for TLS Connections @@ -22,6 +39,9 @@ If you want to also generate new keys, then delete the keys you want to regenera This will trigger the script to generate new keys before generating the corresponding certificate. If you want to regenerate `CA.pem` you must first delete it. +**NOTE: The script genCerts.sh may not be suitable to use for generating keys and certificates for your production environment! ** +**NOTE: Please consult with your Project Security Manager (or similar) on how your keys and certificates shall be generated! ** + ## Java Web Tokens (JWT) KUKSA.val servers use Java Web Tokens (JWT) to validate that a client is authorized to read or write a specific datapoints. diff --git a/kuksa_certificates/Server.pem b/kuksa_certificates/Server.pem index cf8a1be58..4796388ce 100644 --- a/kuksa_certificates/Server.pem +++ b/kuksa_certificates/Server.pem @@ -1,9 +1,9 @@ -----BEGIN CERTIFICATE----- -MIIDxzCCAq+gAwIBAgIUR95d9Q58DithW55HdpMy27QH25kwDQYJKoZIhvcNAQEL +MIIEvDCCA6SgAwIBAgIUficQzSD+1RjwvQQBvs3jGdvrkSgwDQYJKoZIhvcNAQEL BQAwgZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZP dHRhd2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTAT BgNVBAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVj -bGlwc2Uub3JnMB4XDTIzMDMyNDEzNDAwMVoXDTI0MDMyMzEzNDAwMVowgY4xCzAJ +bGlwc2Uub3JnMB4XDTIzMDYxNjEwMDQxNloXDTI0MDYxNTEwMDQxNlowgY4xCzAJ BgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRhd2ExJTAj BgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xDzANBgNVBAMMBlNl cnZlcjEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlwc2Uub3JnMIIBIjAN @@ -13,11 +13,16 @@ g5vatIf3/GVhvxhngNwparLGYXyRV7QCgoBrdzpbTuUhhw51kWzgj1oUw+/XUTre pAvzZa6dh1ieKQSfmJUi6OdcFCagIC1gVAXBv8fPQKjoznmtXs4rmryKfstgtU4Z XHx+wDQaafoUiveZbfVntoi4UW926+IUI6xD4y3wVVoJyiit//T96eJ7QQsfeSvQ GufTSTr3jS+w3oUpG///UZCLdFWfitZk5z3ERGY/ysmGXuRrDKOI1lJsaQIDAQAB -oxUwEzARBgNVHREECjAIggZTZXJ2ZXIwDQYJKoZIhvcNAQELBQADggEBAAi1PLKY -tbdjg6jLbGE3e8D9JUonKPp9q67j1lfqH6H4EiYdu2pTExRA+NPSJUCjEGtHwtFb -/bdDRDZsOvW4XiMpJCRWx8+rZPQpN4OBRuyvxgSBE9e5XnMaHyRVKXW7fEt57rbo -TWXd81QMagdJKHlNdN1FxvpUi957IQN6cZR+1vOI2t3O5EeYCNokikRgw9f6fbf6 -RXKyhnbF/P2uez3NOXQngYrZf4cFd6RLTaQTgLc8JUpP/geNO2/vRpnNbNTjJcvi -f6V4vbRksjxHn37gw8NtgiI1P7TNjQCNNk/bBZHM2q8yMti140DiJKIiz0Vj/lUj -jA8PmIBIxJ/44ZQ= +o4IBCDCCAQQwIgYDVR0RBBswGYIGU2VydmVygglsb2NhbGhvc3SHBH8AAAEwHQYD +VR0OBBYEFGC69Htc/kDNUmsFH8hPB0ZGdkQHMIG+BgNVHSMEgbYwgbOhgZqkgZcw +gZQxCzAJBgNVBAYTAkNBMRAwDgYDVQQIDAdPbnRhcmlvMQ8wDQYDVQQHDAZPdHRh +d2ExJTAjBgNVBAoMHEVjbGlwc2Uub3JnIEZvdW5kYXRpb24sIEluYy4xFTATBgNV +BAMMDGxvY2FsaG9zdC1jYTEkMCIGCSqGSIb3DQEJARYVa3Vrc2EtZGV2QGVjbGlw +c2Uub3JnghR92Q2Epkh+nIVseTvM7XpxzB0XwjANBgkqhkiG9w0BAQsFAAOCAQEA +pGgKVbE6AwnSBmCy/Q1tCSqKQj2ILpOZBNBBpAqbu/vUUy9XL4DKKX/Oi5FnRtJk +VgsBg3jVK5NL00FR7bBb/8WLJHr+lTARk8SFOpReP+8vJap4G6vIJYvmJhvLurvH +axJauc81YnookPMjocjm9WW570UKNR3Yac72lwGcoOUnhLkkGzMvW0/xBoVhaVGh +dPCFSuS65ulpB8TYATyEasrtaC9wYlmOxX/FhkgM7MRsHc4XJyEMmPVxJNBgESL4 +Ca/J0lUvnELAP6GndfXaiJ7VaLp7RbRcHfMVfHsy3buF2pVwr2aMteBOl2WBv7NS +divXduydfcBGtdJ1/a70CA== -----END CERTIFICATE----- diff --git a/kuksa_certificates/genCerts.sh b/kuksa_certificates/genCerts.sh index a76897892..2106fe269 100755 --- a/kuksa_certificates/genCerts.sh +++ b/kuksa_certificates/genCerts.sh @@ -15,9 +15,16 @@ genKey() { openssl genrsa -out $1.key 2048 } +# This method (and how it is called) contains some hacks to pass name verification +# CN is called as per argument +# We also include that as subjectAltName +# We add localhost and 127.0.0.1 as subjectAltName as they are common host names used in test +# Also Server/Client as that can be a work-around (by setting tls-server-name in e.g. kuksa-client) +# as some TLS client integrations cannot handle name verification towards IP-addresses +# (Only client for now in KUKSA.val that has problem with IP host validation is the kuksa-client gRPC integration) genCert() { openssl req -new -key $1.key -out $1.csr -passin pass:"temp" -subj "/C=CA/ST=Ontario/L=Ottawa/O=Eclipse.org Foundation, Inc./CN=$1/emailAddress=kuksa-dev@eclipse.org" - openssl x509 -req -in $1.csr -extfile <(printf "subjectAltName=DNS:$1") -CA CA.pem -CAkey CA.key -CAcreateserial -days 365 -out $1.pem + openssl x509 -req -in $1.csr -extfile <(printf "subjectAltName=DNS:$1, DNS:localhost, IP:127.0.0.1") -CA CA.pem -CAkey CA.key -CAcreateserial -days 365 -out $1.pem openssl verify -CAfile CA.pem $1.pem } diff --git a/kuksa_databroker/README.md b/kuksa_databroker/README.md index 4a8eb46e0..de49db031 100644 --- a/kuksa_databroker/README.md +++ b/kuksa_databroker/README.md @@ -37,7 +37,7 @@ The resulting vss.json can be loaded at startup by supplying the data broker wit --metadata vss.json ``` -## Building +## Building KUKSA.val Databroker Prerequsites: - [Rust](https://www.rust-lang.org/tools/install) @@ -51,12 +51,7 @@ Prerequsites: `cargo build --examples --bins --release` -## Running - -### Databroker -Run the broker with: - -`cargo run --bin databroker` +## Running KUKSA.val Databroker Get help, options and version number with: @@ -75,32 +70,40 @@ OPTIONS: list of files [env: KUKSA_DATA_BROKER_METADATA_FILE=] --jwt-public-key Public key used to verify JWT access tokens --tls-cert TLS certificate file (.pem) - --tls-private-key TLS private key file (.pem) + --tls-private-key TLS private key file (.key) --insecure Allow insecure connections --dummy-metadata Populate data broker with dummy metadata -h, --help Print help information -V, --version Print version information - ``` -To enable authorization, provide a public key used to verify JWT access tokens (provided by connecting clients). +Before starting KUKSA.val Databroker you must decide if you want to use TLS for incoming connections or not. +It is is recommended to use TLS and the you must provide server key by `--tls-private-key` +and server certificate by `--tls-cert`. If you want to run without TLS you must give `--insecure`. -```shell -databroker --jwt-public-key kuksa_certificates/jwt/jwt.key.pub -``` +*Note: Unless stated otherwise, the examples below show KUKSA.val Databroker running in insecure mode!* + +Run the broker in insecure mode with: + +`cargo run --bin databroker -- --insecure` To enable TLS, provide databroker with the path to the (public) certificate and it's corresponding private key. ```shell -databroker --tls-cert kuksa_certificates/Server.pem --tls-private-key kuksa_certificates/Server.key +cargo run --bin databroker -- --tls-cert kuksa_certificates/Server.pem --tls-private-key kuksa_certificates/Server.key ``` +To enable authorization, provide a public key used to verify JWT access tokens (provided by connecting clients). + +```shell +cargo run --bin databroker -- --jwt-public-key kuksa_certificates/jwt/jwt.key.pub --insecure +``` ### :warning: Default port not working on Mac OS The databroker default port `55555` is not usable in many versions of Mac OS. You can not bind it, or if it seems bound you still can not receive messages. Therefore, on Mac OS you need to start databroker on another port, e.g. ``` -databroker --port 55556 +cargo run --bin databroker -- --port 55556 ``` Please note, this also applies if you use a container environment like K3S or Docker on Mac OS. If you forward the port or exposing the host network @@ -110,7 +113,7 @@ For more information see also https://developer.apple.com/forums/thread/671197 Currently, to run databroker-cli (see below), you do need to change the port it connects to in databroker-cli code and recompile it. -### Test the databroker +## Test the Databroker using CLI Run the cli with: @@ -147,7 +150,7 @@ sdv.databroker.v1 > set Vehicle.ADAS.CruiseControl.IsEnabled false [set] OK ``` -#### Authorization +### Authorization ```shell databroker-cli --token-file jwt/read-vehicle-speed.token ``` @@ -164,7 +167,7 @@ or sdv.databroker.v1 > token-file jwt/read-vehicle-speed.token ``` -#### Connect to databroker using TLS +### Connect to databroker using TLS ```shell databroker-cli --ca-cert kuksa_certificates/CA.pem ``` @@ -202,13 +205,22 @@ Subscription is now running in the background. Received data is identified by [1 To change the default configuration use the arguments during startup see [run section](#running) or environment variables. -### Build and run databroker +## Run Databroker test cases + +Use the following command to run Databroker test cases + +```shell + cargo test --all-targets + ``` + +## Build and run Databroker using Docker To build the release version of databroker, run the following command: ```shell RUSTFLAGS='-C link-arg=-s' cargo build --release --bins --examples ``` + Or use following commands for aarch64 ``` cargo install cross @@ -248,7 +260,8 @@ After the image is created the databroker container can be ran from any director ```shell #By default the container will execute the ./databroker command and load the latest VSS file. -docker run --rm -it -p 55555:55555/tcp databroker +# You must explicitly specify that you want insecure mode +docker run --rm -it -p 55555:55555/tcp databroker --insecure ``` To run any specific command, just append you command at the end. diff --git a/kuksa_databroker/createbom/bomutil/maplicensefile.py b/kuksa_databroker/createbom/bomutil/maplicensefile.py index 5d4e2697c..5dc74892a 100644 --- a/kuksa_databroker/createbom/bomutil/maplicensefile.py +++ b/kuksa_databroker/createbom/bomutil/maplicensefile.py @@ -32,5 +32,5 @@ "Zlib": "Zlib.txt.gz", "ISC": "ISC.txt.gz", "ring": "ring.LICENSE.txt.gz", - "webpki": "webpki.LICENSE.txt.gz", + "rustls-webpki": "webpki.LICENSE.txt.gz", } diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 28b0497ae..c38ec1a41 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -115,9 +115,7 @@ async fn main() -> Result<(), Box> { let pem = std::fs::read(ca_cert_filename)?; let ca_cert = tonic::transport::Certificate::from_pem(pem); - let tls_config = tonic::transport::ClientTlsConfig::new() - .ca_certificate(ca_cert) - .domain_name("Server"); + let tls_config = tonic::transport::ClientTlsConfig::new().ca_certificate(ca_cert); client.set_tls_config(tls_config); } diff --git a/kuksa_databroker/databroker/src/main.rs b/kuksa_databroker/databroker/src/main.rs index 291a76fb5..324e795dd 100644 --- a/kuksa_databroker/databroker/src/main.rs +++ b/kuksa_databroker/databroker/src/main.rs @@ -272,7 +272,7 @@ async fn main() -> Result<(), Box> { Arg::new("tls-private-key") .display_order(5) .long("tls-private-key") - .help("TLS private key file (.pem)") + .help("TLS private key file (.key)") .action(ArgAction::Set) .value_name("FILE") .conflicts_with("insecure"), @@ -377,7 +377,11 @@ async fn main() -> Result<(), Box> { "TLS certificate (--tls-cert) must be set if --tls-private-key is.".into(), ); } - (None, None) => ServerTLS::Disabled, + (None, None) => { + return Err( + "You must either provide TLS certificate and key or request insecure mode by --insecure.".into(), + ); + } } }; diff --git a/kuksa_databroker/integration_test/run.sh b/kuksa_databroker/integration_test/run.sh index f62b3cf4b..0e45319de 100755 --- a/kuksa_databroker/integration_test/run.sh +++ b/kuksa_databroker/integration_test/run.sh @@ -25,9 +25,9 @@ DATABROKER_ADDRESS=${DATABROKER_ADDRESS:-"127.0.0.1:55555"} VSS_DATA_DIR="$SCRIPT_DIR/../../data" -echo "Starting databroker container (\"${DATABROKER_IMAGE}\")" +echo "Starting databroker container (\"${DATABROKER_IMAGE}\") in insecure mode" RUNNING_IMAGE=$( - docker run -d -v ${VSS_DATA_DIR}:/data -p 55555:55555 --rm ${DATABROKER_IMAGE} --metadata data/vss-core/vss_release_4.0.json + docker run -d -v ${VSS_DATA_DIR}:/data -p 55555:55555 --rm ${DATABROKER_IMAGE} --metadata data/vss-core/vss_release_4.0.json --insecure ) python3 -m pytest -v "${SCRIPT_DIR}/test_databroker.py"