Skip to content

Commit

Permalink
add initial state store cli (#107)
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Winter <[email protected]>
  • Loading branch information
ryanwinter authored Nov 19, 2024
1 parent be00f31 commit 9202797
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 0 deletions.
31 changes: 31 additions & 0 deletions tools/state-store-cli/generate-credentials.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash

mkdir certs

# create root CA
echo == Creating root CA ==
step certificate create --profile root-ca "my root ca" certs/root_ca.crt certs/root_ca.key

# create intermediate CA
echo == Creating intermediate CA ==
step certificate create --profile intermediate-ca "my intermediate ca" \
certs/intermediate_ca.crt certs/intermediate_ca.key \
--ca certs/root_ca.crt --ca-key certs/root_ca.key

# create client certificate
echo == Creating client certificate ==
step certificate create client \
certs/client.crt certs/client.key \
--not-after 8760h \
--no-password --insecure \
--ca certs/intermediate_ca.crt --ca-key certs/intermediate_ca.key

# create client trust bundle configmap used to validate x509 client connections
kubectl delete configmap client-ca-trust-bundle -n azure-iot-operations
kubectl create configmap client-ca-trust-bundle -n azure-iot-operations \
--from-literal=client_ca.pem="$(cat certs/intermediate_ca.crt certs/root_ca.crt)"

# download the MQTT broker trust bundle
kubectl get secret azure-iot-operations-aio-ca-certificate \
-n cert-manager \
-o jsonpath='{.data.ca\.crt}' | base64 -d > certs/broker-ca.crt
254 changes: 254 additions & 0 deletions tools/state-store-cli/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
# Azure IoT Operations state store CLI tool

The state store CLI tool can be used to manage keys and values in the Azure IoT Operations state store. The tool is a standalone self-sufficient binary application, and doesn't require the installation of additional libraries or frameworks.

Supported platforms:

* Linux (tested on Ubuntu 24.04)
* Windows 11

> [!IMPORTANT]
> The tool is designed to run on a standalone machine with access to the Azure IoT Operations cluster. This is not designed to run on the cluster itself.
## Prerequisites

On the machine where the tool will be run, the following are required:

1. [Install kubectl](https://kubernetes.io/docs/tasks/tools/) which will be used to configure the MQTT Broker

1. Clone the `explore-iot-operations` repository, and enter the state store cli directory:

```shell
git clone https://github.com/Azure-Samples/explore-iot-operations
cd explore-iot-operations/tools/state-store-cli
```

1. Download the state store CLI from the [latest GitHub release](https://github.com/Azure-Samples/explore-iot-operations/releases?q=state-store-cli) and save it in the `state-store-cli` directory

1. **OPTIONAL**: If MQTT broker authentication is required, [install step](https://smallstep.com/docs/step-cli/installation/) to generate the required certificates

## Setup

### Setup with no authentication

If security is not a requirement, due to the cluster being used for non-production purposes, enabling a non-authenticated BrokerListener is the simplest approach.

1. Create a new `BrokerListener` by applying the following:

```shell
kubectl apply -f yaml/listener-open.yaml
```

### Setup with authentication

If security is a requirement, then you will need to expose the MQTT broker using TLS and x509 certificate authentication:

1. Run `generate-credentials.sh` to create the x.509 device certificates and download the MQTT broker trust bundle:

```bash
./generate-credentials.sh
```

1. Inspect the output for errors and make corrections if necessary

1. Edit the following section of `yaml/listener-x509.yaml` and add the correct public DNS or IP address for your kubernetes cluster:

```yaml
san:
dns:
- localhost
ip:
- 127.0.0.1
```

1. Create a new `BrokerListener`::

```shell
kubectl apply -f yaml/listener-x509.yaml
```

> [!NOTE]
> The `certs` directory will contain the following files which will be used the state store cli tool for authenticating with the MQTT broker:
>
> * `broker-ca.crt` : The MQTT broker server certificate
> * `client.crt` : The device certificate for authentication with MQTT broker
> * `client.key` : The device private key for authentication with MQTT broker

## Usage

For accessing help directly from the console just type `statestore --help`.

```shell
$ ./statestore --help
Allows managing key/value pairs in the MQ State Store.
Usage: statestore [OPTIONS] <COMMAND>
Commands:
get Gets the value of an existing key
set Sets a key and value
delete Deletes an existing key and value
help Print this message or the help of the given subcommand(s)
Options:
-n, --hostname <HOSTNAME>
MQ broker hostname
[default: localhost]
-p, --port <PORT>
MQ broker port number
[default: 8883]
--notls
Do not use TLS for connection with MQ broker
-T, --cafile <CAFILE>
Trusted certificate bundle for TLS connection
-C, --certfile <CERTFILE>
Client authentication certificate file
-K, --keyfile <KEYFILE>
Client authentication private key file
-P, --keypasswordfile <KEYPASSWORDFILE>
Password for private key file
--verbose
Verbose logging (errors)
-h, --help
Print help (see a summary with '-h')
-V, --version
Print version
```

Help specific to each command can be printed through calling `statestore <command> --help`.

```shell
$ ./statestore get --help
Gets the value of an existing key
Usage: statestore get [OPTIONS] --key <KEY>
Options:
-k, --key <KEY>
Device State Store key name to retrieve
-f, --valuefile <VALUEFILE>
File where to write the key value. If not provided, the value is written to stdout
-n, --hostname <HOSTNAME>
MQ broker hostname [default: localhost]
-p, --port <PORT>
MQ broker port number [default: 8883]
--notls
Do not use TLS for connection with MQ broker
-T, --cafile <CAFILE>
Trusted certificate bundle for TLS connection
-C, --certfile <CERTFILE>
Client authentication certificate file
-K, --keyfile <KEYFILE>
Client authentication private key file
-P, --keypasswordfile <KEYPASSWORDFILE>
Password for private key file
--verbose
Verbose logging (errors)
-h, --help
Print help
```

## Examples

For the examples below, assume:

- The MQTT broker is named `mybroker.net`, on port `8883` or `1883`
- The MQTT brokers trusted CA certificate bundle is available locally as saved locally at `./certs/broker-ca.crt`
- Client certificates are set in the AIO MQ Broker, and saved locally at `./certs/client.crt` and `./certs/client.key`.

### X.509 authentication with TLS

To retrieve an existing key:

```shell
./statestore get -n "mybroker.net" -k "keyName1" -f "./keyValue1.txt" -T "./certs/broker-ca.crt" -C "./certs/client.crt" -K "./certs/client.key"
```

|||
|-|-|
|Outcome|Prints the value of an existing key to the console.</br>If `--valuefile` argument is provided, the value is written to the provided file if the key exists.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port, bad CA certificate).</br>- Authentication failures (bad certificates)</br>- The key does not exist.</br>- Cannot write value to file (if `--valuefile` is used).|

To set the value of a key:

```shell
./statestore set -n "mybroker.net" -k "keyName1" --value "keyValue1" -T "./certs/broker-ca.crt" -C "./certs/client.crt" -K "./certs/client.key"
```

|||
|-|-|
|Outcome|Sets the value of a key in the state store.</br>If `--valuefile` (short, `-f`) argument is provided (instead of `--value`), the value is read from the provided file.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port, bad CA certificate).</br>- Authentication failures (bad certificates)</br>- Cannot read file (if `--valuefile` is used).|

To delete an existing key:

```shell
./statestore delete -n "mybroker.net" -k "keyName1" -T "./certs/broker-ca.crt" -C "./certs/client.crt" -K "./certs/client.key"
```

|||
|-|-|
|Outcome|Deletes an existing key in the state store.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port, bad CA certificate).</br>- Authentication failures (bad certificates)</br>- Key does not exist.|

### No authentication, no TLS

> [!CAUTION]
> The use of non-secure connections with the MQTT broker is highly discouraged. The option is provided for testing purposes only and it is recommended to use secure connections in production environments.

To retrieve an existing key:

```shell
./statestore get -n "mybroker.net" -k "keyName1" -f "./keyValue1.txt" --notls
```

|||
|-|-|
|Outcome|Prints the value of an existing key to the console.</br>If `--valuefile` argument is provided, the value is written to the provided file if the key exists.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port).</br>- The key does not exist.</br>- Cannot write value to file (if `--valuefile` is used).|

To set the value of a key:

```shell
./statestore set -n "mybroker.net" -k "keyName1" --value "keyValue1" --notls
```

|||
|-|-|
|Outcome|Sets the value of a key in the state store.</br>If `--valuefile` (short, `-f`) argument is provided (instead of `--value`), the value is read from the provided file.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port).</br>- Cannot read file (if `--valuefile` is used).|

To delete an existing key:

```shell
./statestore delete -n "mybroker.net" -k "keyName1" --notls
```

|||
|-|-|
|Outcome|Deletes an existing key in the state store.|
|Return|Zero (0) on success, non-zero on error.|
|Possible errors|- Not able to connect (no internet, bad hostname and/or port).</br>- Key does not exist.|

## Limitations

The following features are **not** supported by the state store CLI tool:

- `vdel`, `observer` and `unobserve` operations
- SAT authentication
11 changes: 11 additions & 0 deletions tools/state-store-cli/yaml/listener-open.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: mqttbroker.iotoperations.azure.com/v1
kind: BrokerListener
metadata:
name: listener-open
namespace: azure-iot-operations
spec:
brokerRef: default
serviceType: LoadBalancer
serviceName: aio-open-listener
ports:
- port: 1883
34 changes: 34 additions & 0 deletions tools/state-store-cli/yaml/listener-x509.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
apiVersion: mqttbroker.iotoperations.azure.com/v1
kind: BrokerListener
metadata:
name: listener-x509
namespace: azure-iot-operations
spec:
brokerRef: default
serviceType: LoadBalancer
ports:
- port: 8883
authenticationRef: default-x509
tls:
mode: automatic
certManagerCertificateSpec:
issuerRef:
name: azure-iot-operations-aio-certificate-issuer
kind: ClusterIssuer
group: cert-manager.io
san:
dns:
- localhost
ip:
- 127.0.0.1
---
apiVersion: mqttbroker.iotoperations.azure.com/v1
kind: BrokerAuthentication
metadata:
name: default-x509
namespace: azure-iot-operations
spec:
authenticationMethods:
- method: X509
x509Settings:
trustedClientCaCert: client-ca-trust-bundle

0 comments on commit 9202797

Please sign in to comment.