README.md 6.43 KB
Newer Older
Trishna Saeharaseelan's avatar
Trishna Saeharaseelan committed
1 2
# Communications Backbone

3 4
Communications Backbone by C2 Team (NOC)

5 6 7 8 9 10 11 12
### Infrastructure 

The backbone has 3 runtime components: 

- RabbitMQ - run as a docker container 
- The bus which handles the delivery of messages between queues 
- The API which provides an interface to read from and write to the queues

13 14
### Data flow 

15
- Client A sends to `client-a-outbox` (by POSTing to API /send)
16 17 18
- Messages are forwarded from `client-a-outbox` to `soar-publish` 
- Messages are published from `soar-publish` with the given topic read from the message 
- Subscribers listen to for messages 
19
- Subscription is delivered to `client-b-inbox` (if the client subscription matches the message topic) 
Dan Jones's avatar
Dan Jones committed
20
- Client B reads from `client-b-inbox` (by GETting from API /receive)
21

22 23
There is a parallel flow when a client sends to `client-a-broadcast` (by POSTing to /notify). 
In this case the messages are delivered through the broadcast exchange to all clients `client-x-inbox`.
24

25
### Prerequisites 
26

27 28 29
- Python >= 3.8
- A virtual environment manager - virtualenv, venv or pipenv 
- Docker and docker-compose 
30

31
### Running via docker-compose
32 33

```
34
run-compose.sh
35 36
```

37 38 39 40 41 42
Using `docker-compose` will mean that everything is setup automatically, this includes the `rabbitmq` container, the backbone API, and the backbone bus. The `run-compose.sh` script has been provided to simplify this even further - all you have to do is set whatever env vars you need in the `.env` file and then run `./run-compose.sh` (the defaults in `.env` are fine for local dev work, but ones not labelled `optional` will need setting in a production setting). The env vars are:

- `DATA_DIR` - Where to mount the volume of the API container on your local system. This defaults to the result of `pwd`, which should be within the `communications-backbone` repo
- `SOAR_TOKEN_LIFETIME` (Optional) - The number of hours until a newly created token expires
- `SOAR_TOKEN_SECRET` (Optional) - A secret key used to encrypt/decrypt token data. If specified the value should be set using TokenModel.getKey()

43
### Running the bus and API natively (without docker)
44 45

#### Setup 
46

47 48 49 50
We recommend using some form of python virtual environment to maintain a consistent 
python version and ring-fence the package management.

In your virtual environment: 
51

52
```
53
pip install -r requirements-dev.txt  
54 55
```

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
This installs both the development and runtime dependencies for the project.

### Testing 

Current coverage: 

- API: yes 
- Pika RabbitMQ implementation: no

In your virtual environment: 

```
pytest
```

71 72 73 74 75 76
#### RabbitMQ 

`docker run --rm -p 5672:5672 -d --hostname rmq --name rmq rabbitmq:management`

#### API 

77 78
In your virtual environment:

79
```
80
python api.py 
81 82 83 84
```

#### Event bus 

85 86
In your virtual environment: 

87
``` 
88
python soar_bus.py
89 90
```

91 92 93 94
At present the soar bus creates the clients defined in the API when it starts 
but does not monitor for new clients so if you create a new client you will 
need to restart the bus. This will be fixed in a later release.

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
### Client Adapters 

The intended use of the backbone is that multiple clients connect to the backbone 
using adapters. An adapter handles: 

- Authentication 
- Sending and receiving over the backbone API 
- Transforming messages between local formats and the backbone message formats

We have implemented the following template client adapters: 

- [Javascript](https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-javascript)
- [Python](https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-python)

If you need to port the adapter to another language please contact us. 

The adapters can be installed as packages and sub-classed as required for your 
client application.

For install and usage instructions for the adapters see the READMEs. 

116 117
### Usage

118 119 120 121 122 123 124 125
- Run the backbone 
- Create some clients
- Restart the soar_bus service 
  - `cd docker && docker-compose restart soar_bus` 
- Save your client credentials 
- Test reading and writing directly to the queues
- Create an adapter 
- Test sending and receiving via the adapter    
126

Dan Jones's avatar
Dan Jones committed
127 128 129 130 131 132 133 134 135 136 137 138 139
#### Create some clients

##### With the script 

```bash 
python client_create.py
# will create a default 'admin' client subscribed to all messages (#)
# OR 
python client_create.py --id=[client_id] --name="Your Client Name" --sub="something.something.#"
# will create a client with your preferred name, id and subscription
```

##### Through the API 
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
`POST` to `http://localhost:8087/client`

The POST body should be JSON. 

```json
{ 
  "client_id": "noc-c2",
  "client_name": "NOC C2", 
  "subscription": "soar.*.noc.#"
}
```

- `client_id` - a project unique human readable name 
- `client_name` - how to refer to that client on screen 
- `subscription` - the topic pattern identifying the messages you want to receive 

(`*` = single word wildcard, `#` = multi-word wildcard)

The response from the post contains your client `secret`. This is only displayed once. 

Subsequent GETS to `/client` or `/client/[client-id]` will not return the secret. 

You should save the response as `soar-config.json` adding the additional `api` field 
to specify the root URL of the API you're connecting to 
(eg http://localhost:8087 for local development)
166

167 168 169 170
#### Send / Receive directly 

```
# Send a message 
171
python client_send.py noc-c2-outbox 'soar.noc.slocum.something' from noc-c2
172 173 174 175
```

```
# Receive messages  
176
python client_read.py noc-sfmc-inbox
177 178
```

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
These scripts bypass authentication reading and writing directly to rabbitmq.

#### Authentication 

Authentication is handled by a client credentials grant which is a GET 
request to `/token?client_id=[client-id]&secret=[secret]`

The response includes a token and this token should be included as an 
authorization header in requests to the API: 

```json
{
  "Authorization": "Bearer [token]"
}
```

#### Send and Receive via API

Once you have a bearer token you can make requests to send and receive 
messages via the API: 

- GET `/receive?max_messages=[X]` - gets all messages in the queue 
  - the default value of `max_messages` if not specified is 10
- POST `/send` - publish a message to a given topic 
- POST `/notify` - broadcast a message to all clients 

Supplying the token as an Authorization header as described above.

#### Send and Receive using adapters 
208

209 210
If you have implemented one of the adapter templates then authentication 
is handled for you.
211

212 213
You will need to create a set of client credentials as above using the 
POST `/client` endpoint and save the response. 
214

215
Then you pass your credentials when you create your adapter instance.