github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/docs/content/previews/protov2/_index.md (about) 1 +++ 2 title = "V2 Protocol & Security" 3 toc = true 4 weight = 10 5 pre = "<b>1. </b>" 6 +++ 7 8 This is a guide for early adopters who wish to test and study the [Version 2 Protocol and Security](/adr/001/) project. 9 10 {{% notice warning %}} 11 This is a *Hard Mode* guide that does everything manually and with no Configuration Management. 12 {{% /notice %}} 13 14 ### Requirements 15 16 * Choria Nightly 17 * [Choria AAA Service](https://choria-io.github.io/aaasvc/) Nightly 18 * [Choria Provisioner](https://choria-io.github.io/provisioner/) Nightly 19 * Docker on Intel CPU 20 21 ### Security Credentials 22 23 Security is by means of a ed25519 key that signs JWTs, some JWTs form a chain and can sign others. Regardless of the signer verification can be done using the public key associated with the Organization Issuer. 24 25  26 27 The Organization Issuer can be kept offline with Provisioning and AAA being delegated authorities capable of signing servers and clients but these are optional components - the Organization Issuer can directly sign Clients and Servers allowing them to operate without the other central components. 28 29 ### Deployment Methods 30 31 We demonstrate two deployment methods: 32 33 * [Decentralized](#decentralized-deployment) - like traditional Choria with only a broker as shared component 34 * [Centralized AAA and Provisioning](#self-provisioned-and-aaa-integrated-deployment) - uses Choria AAA Service and Choria Provisioner for low-touch auto enrolment of Clients and Servers 35 36 Additionally, we show how Hashicorp Vault can be integrated to [manage the Organization Issuer](#vault-as-organization-issuer) 37 38 {{% notice tip %}} 39 We recommend reviewers really dig into the details here, we do have a [Docker Compose environment](https://github.com/ripienaar/choria-compose) with this all setup. 40 {{% /notice %}} 41 42 ### Decentralized deployment 43 44 In this model we will deploy a system that resembles the basic architecture diagram below 45 46  47 48 We have only the Brokers as central architecture with no Central AAA or Provisioning. 49 50 We will not use mTLS in this case. mTLS is supported but a major advantage of this mode is that it's not required. 51 52 #### Docker 53 54 We will need two Docker networks and 3 instances - broker, server, client and issuer. 55 56 ```nohighlight 57 $ docker network create choria_v2proto 58 $ docker network create choria_issuer 59 $ docker pull registry.choria.io/choria-nightly/choria:nightly 60 $ docker run -ti --rm --entrypoint bash \ 61 --network choria_v2proto \ 62 --hostname broker.example.net \ 63 registry.choria.io/choria-nightly/choria:nightly -l 64 $ docker run -ti --rm --entrypoint bash \ 65 --network choria_v2proto \ 66 --hostname server.example.net \ 67 registry.choria.io/choria-nightly/choria:nightly -l 68 $ docker run -ti --rm --entrypoint bash \ 69 --network choria_v2proto \ 70 --hostname client.example.net \ 71 registry.choria.io/choria-nightly/choria:nightly -l 72 $ docker run -ti --rm --entrypoint bash \ 73 --network choria_issuer \ 74 --hostname issuer.example.net registry.choria.io/choria-nightly/choria:nightly -l 75 ``` 76 77 The issuer is not needed per-se but will demonstrate that the Issuer credentials never need to near the managed network. 78 79 #### Keys and JWTs 80 81 In this Scenario we need: 82 83 * An Organization Issuer keypair 84 * A client JWT for each user 85 * A server JWT for each server 86 * x509 certificate for the broker TLS port 87 88 In this scenario you are responsible for creating and distributing the keys and tokens. 89 90 ##### Organization Issuer 91 92 The Organization Issuer is the root of the Trust Chain and is a ed25519 key, let's create the keypair on the issuer node: 93 94 ```nohighlight 95 [choria@issuer ~]$ mkdir -p development/issuer 96 [choria@issuer ~]$ choria jwt keys development/issuer/private.key development/issuer/public.key 97 Public Key: b3989a299278750427b00213693c2ca02146476a361667682446230842836da8 98 99 Ed25519 seed saved in development/issuer/private.key 100 ``` 101 102 {{% notice warning %}} 103 This key should be kept private and ideally in a Hashicorp Vault server. See a later section for guidance on Vault. 104 {{% /notice %}} 105 106 ##### Broker JWT and Config 107 108 Every broker needs a ed25519 Keypair and a signed JWT. 109 110 First we create a keypair on the broker, the private key never leaves the broker: 111 112 ```nohighlight 113 [choria@broker ~]$ choria jwt keys /etc/choria/private.key /etc/choria/public.key 114 Public Key: 8918c1c7a4aeb4d4ad16729dc9b9c12df021d9296106eb5f072b224aa8f8eee9 115 116 Ed25519 seed saved in /etc/choria/private.key 117 ``` 118 119 Pass the Public Key to the Organization Issuer who creates a JWT: 120 121 ```nohighlight 122 [choria@issuer ~]$ mkdir -p development/broker/broker.example.net 123 [choria@issuer ~]$ choria jwt server development/broker/broker.example.net/token.jwt \ 124 broker.example.net \ 125 8918c1c7a4aeb4d4ad16729dc9b9c12df021d9296106eb5f072b224aa8f8eee9 \ 126 development/issuer/private.key \ 127 --collectives=choria 128 ``` 129 130 With access to just the Broker public key the Organization Issuer can create a server token, pass this back to the server who stores it in `/etc/choria/broker.jwt`. 131 132 {{% notice tip %}} 133 Note that for version 2 protocol the default collective is `["choria"]`. 134 {{% /notice %}} 135 136 ```nohighlight 137 [choria@issuer ~]$ choria jwt development/broker/broker.example.net/token.jwt development/issuer/public.key 138 Validated Server Token development/server/server.example.net/token.jwt 139 140 Identity: server.example.net 141 Expires At: 2023-12-08 13:03:23 +0000 UTC (364d23h59m41s) 142 Collectives: choria 143 Public Key: 8918c1c7a4aeb4d4ad16729dc9b9c12df021d9296106eb5f072b224aa8f8eee9 144 Organization Unit: choria 145 Private Network ID: 92328d88bef9d063480fd4b0ec5e4879 146 147 Broker Permissions: 148 149 No server specific permissions granted 150 ``` 151 152 We pass the JWT back to the broker and save in `/etc/choria/broker.jwt`. 153 154 The broker need x509 certificates to open the TLS network port, here we just self-sign one but you can get those from anywhere. 155 156 ```nohighlight 157 [choria@broker ~]$ openssl genrsa -out /etc/choria/broker-tls.key 2048 158 Generating RSA private key, 2048 bit long modulus (2 primes) 159 ..+++++ 160 ....................................................................................................+++++ 161 e is 65537 (0x010001) 162 [choria@broker ~]$ openssl req -new -x509 -sha256 -key /etc/choria/broker-tls.key \ 163 -out /etc/choria/broker-tls.cert -days 365 -subj "/O=Choria.io/CN=broker.example.net" 164 ``` 165 166 With all in place it should look like this: 167 168 ```nohighlight 169 [choria@broker ~]$ find /etc/choria/ 170 /etc/choria/ 171 /etc/choria/broker.conf 172 /etc/choria/broker-tls.key 173 /etc/choria/broker-tls.cert 174 /etc/choria/private.key 175 /etc/choria/public.key 176 /etc/choria/broker.jwt 177 ``` 178 179 We create the broker configuration in `/etc/choria/broker.conf` and start it, you need to change your issuer here: 180 181 ```nohighlight 182 # The name of the organization to configure, for now only supports choria 183 plugin.security.issuer.names = choria 184 185 # The public key from the issuer 186 plugin.security.issuer.choria.public = b3989a299278750427b00213693c2ca02146476a361667682446230842836da8 187 188 # Configures access to broker internal statistics and more 189 plugin.choria.network.system.password = sYst3m 190 191 # Used later in the Provisioner based setup 192 plugin.choria.network.provisioning.client_password = s3cret 193 194 plugin.choria.stats_port = 8222 195 plugin.choria.broker_network = true 196 plugin.choria.network.client_port = 4222 197 plugin.choria.network.stream.store = /data 198 plugin.choria.network.system.user = system 199 loglevel = info 200 plugin.choria.use_srv = false 201 202 plugin.security.provider = choria 203 plugin.security.choria.certificate = /etc/choria/broker-tls.cert 204 plugin.security.choria.key = /etc/choria/broker-tls.key 205 plugin.security.choria.token_file = /etc/choria/broker.jwt 206 plugin.security.choria.seed_file = /etc/choria/private.key 207 ``` 208 209 Let's start the broker, showing the key lines from the output here: 210 211 ```nohighlight 212 $ choria broker run --config /etc/choria/broker.conf 213 INFO[0000] Choria Broker version 0.99.0.20221201 starting with config /etc/choria/broker.conf 214 INFO[0000] Starting Network Broker 215 WARN[0000] Allowing unverified TLS connections for Organization Issuer issued connections component=network 216 WARN[0000] Loaded Organization Issuer choria with public key b3989a299278750427b00213693c2ca02146476a361667682446230842836da8 component=network 217 INFO[0000] Listening for client connections on [::]:4222 component=network_broker 218 ... 219 ``` 220 221 ##### Server JWT 222 223 Every server needs a ed25519 Keypair and a signed JWT. 224 225 The server process is identical to the broker process except change `broker.example.net` to `server.example.net` in identities and make obvious file name changes. Servers do not need any x509 certificates like brokers. 226 227 Now we can configure and start the server, place this in `/etc/choria/server.conf`: 228 229 ```nohighlight 230 # The name of the organization to configure, for now only supports choria 231 plugin.security.issuer.names = choria 232 233 # The public key from the issuer 234 plugin.security.issuer.choria.public = b3989a299278750427b00213693c2ca02146476a361667682446230842836da8 235 236 # We enable authorization and set it to trust the JWT tokens policy 237 rpcauthorization = 1 238 rpcauthprovider = aaasvc 239 240 plugin.security.provider = choria 241 plugin.security.choria.token_file = /etc/choria/server.jwt 242 plugin.security.choria.seed_file = /etc/choria/private.key 243 plugin.choria.middleware_hosts = nats://broker.example.net:4222 244 ``` 245 246 And finally let's run the server, showing key log lines only: 247 248 {{% notice tip %}} 249 Servers usually run as root, here as the `choria` user as it's in the container 250 {{% /notice %}} 251 252 ```nohighlight 253 [choria@server ~]$ choria server run --config /etc/choria/server.conf 254 INFO[0000] Choria Server version 0.99.0.20221201 starting with config /etc/choria/server.conf using protocol version 2 255 INFO[0000] Setting JWT token and unique reply queues based on JWT for "server.example.net" component=server connection=server.example.net identity=server.example.net 256 INFO[0000] Setting custom inbox prefix based on unique ID to choria.reply.77e64440ac709c0836487e5b77334e5b component=server connection=server.example.net identity=server.example.net 257 ``` 258 259 ###### Client JWT 260 261 Every client needs a ed25519 keypair and a signed JWT. 262 263 We will create a client that has access to Choria Streams and the ability to manage the fleet without any AAA Server. 264 265 The client will create their own keypair, so we run that in the client node: 266 267 ```noghighlight 268 [choria@client ~]$ mkdir -p ~/.config/choria/ 269 [choria@client ~]$ choria jwt keys ~/.config/choria/private.key ~/.config/choria/public.key 270 Public Key: 4bbfddb9f70f4b39f5b13bac8e83a9a31c3af49e388da86a666f8615101bc818 271 272 Ed25519 seed saved in /home/choria/.config/choria/private.key 273 ``` 274 275 This client `private.key` should be kept private and not shared, the JWT can be created with knowledge of the public key only. 276 277 The client pass their Public Key to the Organization Issuer who creates a JWT on the Issuer node: 278 279 {{% notice tip %}} 280 Here we use `choria` as the identity, this would match the unix user name. 281 282 If a user is on many machines, create a JWT per machine. 283 {{% /notice %}} 284 285 ```nohighlight 286 [choria@issuer ~]$ mkdir -p development/client/choria 287 [choria@issuer ~]$ choria jwt client development/client/choria/token.jwt choria development/issuer/private.key \ 288 --public-key 4bbfddb9f70f4b39f5b13bac8e83a9a31c3af49e388da86a666f8615101bc818 \ 289 --stream-admin \ 290 --event-viewer \ 291 --elections-user \ 292 --service \ 293 --fleet-management \ 294 --agents '*' \ 295 --validity 1y 296 Saved token to development/client/choria/token.jwt, use 'choria jwt view development/client/choria/token.jwt' to view it 297 ``` 298 299 With access to the Issuer private key, but not the user private key, we can create a JWT for the user. Since we have no AAA Service we mark this user as a `service` which allows them to have a long token validity. We set a policy allowing all agent access, in real life this would be an Open Policy Agent policy. 300 301 ```nohighlight 302 [choria@issuer ~]$ choria jwt development/client/choria/token.jwt development/issuer/public.key 303 Validated Client Identification Token development/client/choria/token.jwt 304 305 Caller ID: choria 306 Organization Unit: choria 307 Allowed Agents: * 308 Public Key: 4bbfddb9f70f4b39f5b13bac8e83a9a31c3af49e388da86a666f8615101bc818 309 Private Network ID: 0a63c70a8817f5ef4d19d055ce6513f1 310 Expires At: 2023-12-08 12:54:07 +0000 UTC (364d23h59m19s) 311 312 Client Permissions: 313 314 Can manage Choria fleet nodes 315 Can use Leader Elections 316 Can view Lifecycle and Autonomous Agent events 317 Can administer Choria Streams 318 Can access the Broker system account 319 Can have an extended token lifetime 320 ``` 321 322 Pass the JWT back to the client who saves it in `~/.config/choria/token.jwt`. 323 324 ```nohighlight 325 [choria@client ~]$ find ~/.config 326 /home/choria/.config 327 /home/choria/.config/choria 328 /home/choria/.config/choria/private.key 329 /home/choria/.config/choria/public.key 330 /home/choria/.config/choria/token.jwt 331 ``` 332 333 We create a system-wide client configuration in `/etc/choria/client.conf`: 334 335 ```nohighlight 336 loglevel = warn 337 plugin.choria.middleware_hosts = broker.example.net:4222 338 plugin.choria.network.system.user = system 339 plugin.choria.network.system.password = sYst3m 340 341 plugin.security.provider = choria 342 plugin.security.choria.token_file = ~/.config/choria/token.jwt 343 plugin.security.choria.seed_file = ~/.config/choria/private.key 344 ``` 345 346 We can now test the client: 347 348 ```nohighlight 349 [choria@client ~]$ choria ping 350 server.example.net time=3 ms 351 352 ---- ping statistics ---- 353 1 replies max: 4ms min: 4ms avg: 4ms overhead: 12ms 354 ``` 355 356 Other commands like `choria req choria_util info` should work demonstrating authorization works and `choria broker server list` should list the broker indicating Broker System Account access works. After a minute or so `choria broker stream ls` will show a list of Streams demonstrating Choria Streams authority worked. 357 358 ### Self Provisioned and AAA Integrated Deployment 359 360 Thus far we had to manually sign and configure every single server and client, we had to copy files around and more, it's all a bit tedious. 361 362 Lets see how Choria can configure itself and how user management can be centralized for self-service user enrollment. 363 364 * Instead of signing Server JWTs servers will go to [Choria Provisioner](https://choria-io.github.io/provisioner/) to obtain credentials and configuration 365 * Instead of issuing Client JWTs for every user that are long-lasting we will use a central authorization flow to issue short-lived JWTs and distribute them 366 367  368 369 #### Docker 370 371 We will create re-use the networks we made before, and we can keep the same `issuer.example.net` and `broker.example.net`. 372 373 So stop and recreate your server and client containers, we'll 2 more container during the guide. 374 375 #### Issuer 376 377 The issuer is unchanged from before, so just follow the same steps as before or keep the one you have if you followed the Decentralized section. 378 379 #### Broker 380 381 The broker is unchanged from before, so just follow the same steps as before or keep the one you have if you followed the earlier Decentralized section. 382 383 You will note we have a setting in the `broker.conf`: 384 385 ```ini 386 plugin.choria.network.provisioning.client_password = s3cret 387 ``` 388 389 This instructs the broker that we will be connecting servers needing provisioning to it. You should see a log line like: 390 391 ```nohighlight 392 WARN[0000] Allowing Provisioner connections subject to JWT claims component=network 393 ``` 394 395 #### Provisioner 396 397 The [Choria Provisioner](https://choria-io.github.io/provisioner/) is a service that configures new Choria Servers: 398 399 * Enrolls servers into the Issuer 400 * Create a per-node configuration 401 * Deploys Open Policy Agent policies 402 * Configures the server 403 * Optionally perform version upgrades 404 405 We have CLI tooling allowing you to re-provision servers on demand and more. Review its documentation for full detail. 406 407 It needs to connect to the broker, so it needs JWT token, let's create the container and create the private key: 408 409 Since the container does not have the `choria` command we have to jump some hoops, we'll make a local storage directory for its configuration and keys and then mount that in. 410 411 ```nohighlight 412 host$ mkdir provisioner 413 host$ docker run -v `pwd`/provisioner:/etc/choria-provisioner --user root --entrypoint bash --rm -ti registry.choria.io/choria-nightly/choria:nightly -l 414 [root@5d96691fa69f /]# choria jwt keys /etc/choria-provisioner/private.key /etc/choria-provisioner/public.key 415 Public Key: a8c15c0a4bbae0646d0c5aa92513f4d58c2c0e51464b4b267bb3a42dbebd1c8a 416 [root@5d96691fa69f /]# chown -R choria:choria /etc/choria-provisioner/ 417 [root@5d96691fa69f /]# exit 418 ``` 419 420 Next we create a provisioner JWT and save it in `provisioner/token.jwt` 421 422 ```nohightlight 423 [choria@issuer ~]$ mkdir -p development/provisioner 424 [choria@issuer ~]$ choria jwt client development/provisioner/token.jwt provisioner_signer development/issuer/private.key \ 425 --public-key a8c15c0a4bbae0646d0c5aa92513f4d58c2c0e51464b4b267bb3a42dbebd1c8a \ 426 --server-provisioner \ 427 --validity 365d \ 428 --issuer 429 ``` 430 431 Here we create a token that has access to the NATS Account new machines will join, it's a year valid and it can issue new credentials. 432 433 Place the `development/provisioner/token.jwt` on your host in `provisioner/token.jwt`, next to `private.key` and `public.key` we made above. 434 435 {{% notice tip %}} 436 We will not delve much into the Provisioner configuration details, [visit its documentation site](https://choria-io.github.io/provisioner/) for details. 437 {{% /notice %}} 438 439 We need to configure how the Provisioner use these files, create `provisioner/client.cfg` on your host: 440 441 ```ini 442 plugin.security.provider = choria 443 plugin.security.choria.token_file = /etc/choria-provisioner/token.jwt 444 plugin.security.choria.seed_file = /etc/choria-provisioner/private.key 445 446 identity = provisioner_signer 447 448 plugin.choria.middleware_hosts = nats://broker.example.net:4222 449 ``` 450 451 Next we create the Provisioner configuration file in `provisioner/choria-provisioner.yaml`: 452 453 ```yaml 454 # The issuer public key 455 jwt_verify_cert: b3989a299278750427b00213693c2ca02146476a361667682446230842836da8 456 interval: 1m 457 logfile: /dev/stdout 458 loglevel: info 459 helper: /etc/choria-provisioner/helper.rb 460 token: s3cret 461 choria_insecure: false 462 site: PREVIEW 463 broker_provisioning_password: s3cret 464 jwt_signing_key: private.key 465 jwt_signing_token: token.jwt 466 467 features: 468 jwt: true 469 ed25519: true 470 ``` 471 472 Next we need the script that generates per-node configuration, store the [helper.rb](helper.rb) in `provisioner/helper.rb` and change the `ISSUER` constant near the top. 473 474 ```nohightlight 475 host$ vi provisioner/helper.rb 476 host$ chmod a+x provisioner/helper.rb 477 host$ sudo chown -R 2048:2048 provisioner 478 ``` 479 480 We can now run our Provisioner: 481 482 ```nohighlight 483 host$ docker run -ti --rm -v `pwd`/provisioner:/etc/choria-provisioner \ 484 --network choria_v2proto \ 485 --hostname provisioner.example.net \ 486 choria/provisioner:nightly 487 ``` 488 489 #### Servers 490 491 For servers, we are going to need the RPM (already in the container) and a new file `/etc/choria/provisioning.jwt`. This is read by the server process and tells it to enter provisioning mode. 492 493 The JWT file is basically just a configuration file signed by our Issuer. The server reads it unvalidated but the Provisioner will ensure the incoming server holds the token signed by our Issuer. 494 495 ```nohighlight 496 [choria@issuer ~]$ choria jwt prov development/server/provisioning.jwt \ 497 development/issuer/private.key \ 498 --token s3cret \ 499 --urls nats://broker.example.net:4222 \ 500 --protocol-v2 \ 501 --default 502 Saved token to development/server/provisioning.jwt, use 'choria jwt view development/server/provisioning.jwt' to view it 503 504 [choria@issuer ~]$ choria jwt development/server/provisioning.jwt 505 Unvalidated Provisioning Token development/server/provisioning.jwt 506 507 Token: ***** 508 Secure: false 509 URLS: nats://broker.example.net:4222 510 Provisioning by default: true 511 Using version 2 Protocol: true 512 Server Version Upgrades: false 513 Standard Claims: { 514 "purpose": "choria_provisioning", 515 "iss": "Choria Tokens Package v0.99.0.20221210", 516 "sub": "choria_provisioning", 517 "nbf": 1670850426, 518 "iat": 1670850426, 519 "jti": "60a2973b10304184b997f9ea50eeb7a4" 520 } 521 ``` 522 523 Copy this to your host before running the server. We need to mount this token into the server containers, no other configuration is needed: 524 525 ```nohighlight 526 host$ docker run -ti --rm \ 527 --network choria_v2proto \ 528 --hostname server.example.net \ 529 -v `pwd`/provisioning.jwt:/etc/choria/provisioning.jwt \ 530 registry.choria.io/choria-nightly/choria:nightly server run --config /etc/choria/server.conf 531 ``` 532 533 The server will now start and connect to the Broker, communicate with the Provisioner and restart itself. After restart 534 the client (configured next) will be able to communicate with it. 535 536 Previously we had to use `choria jwt keys` and `choria jwt server` to create Private keys and to issue a signed JWT and then manually transfer that to the Server and configure the Server. This all happens under Choria Provisioner control and takes just a few milliseconds. The only site-unique part about a machine is now the `provisioner.jwt` that is shared by your fleet, so it's easily placed there during base image build or configuration management. You could issue node-unique `provisioning.jwt` files with extended information in them and in your `helper.rb` perform additional validation if you needed that much control. 537 538 #### AAA Service 539 540 To provide a self-service system for Clients configure the [Choria AAA Service](https://choria-io.github.io/aaasvc/). Here we will configure it to both issue JWTs and Sign individual requests - meaning it's required to be available for every RPC request. The signing part is optional though, and we could skip that, using it only to obtain JWT tokens. 541 542 {{% notice tip %}} 543 We will not delve much into the AAA Service configuration details, [visit its documentation site](https://choria-io.github.io/aaasvc/configuration/org-issuer/) for details. 544 {{% /notice %}} 545 546 We need to issue 3 sets of credentials here: 547 548 * One to sign users who request their JWT using `choria login` called the *Chain Signer*, since this is HTTP it also needs a x509 certificate 549 * One to sign RPC requests on behalf of users after evaluating policies and auditing requests called the *Request Signer* 550 * One to connect to Choria Broker with and run a Choria RPC Service that will receive requests from users to sign their requests called a *Signer Service*. 551 552 Like the Provisioner the AAA Service container does not have the `choria` binary, so we need to jump some hoops to make the keys and configuration: 553 554 ```nohighlight 555 host$ docker run -ti --rm -v `pwd`/aaasvc:/etc/aaasvc --user root --entrypoint bash registry.choria.io/choria-nightly/choria:nightly -l 556 [root@38f75c90e475 /]# openssl genrsa -out /etc/aaasvc/https-private.key 2048 557 Generating RSA private key, 2048 bit long modulus (2 primes) 558 ........................................................................................+++++ 559 ...................................+++++ 560 [root@38f75c90e475 /]# openssl req -new -x509 -sha256 -key /etc/aaasvc/https-private.key -out /etc/aaasvc/https-public.crt -days 365 -subj "/O=Choria.io/CN=aaa.choria.local" 561 [root@38f75c90e475 /]# choria jwt keys /etc/aaasvc/chain-signer-private.key /etc/aaasvc/chain-signer-public.key 562 Public Key: 17807f2c5fa959383ee5851813863426525c081f6464556e5dec482e815caded 563 564 Ed25519 seed saved in /etc/aaasvc/chain-signer-private.key 565 ``` 566 567 We create a self-signed x509 certificate since the Authentication service runs over HTTPS, you can use any certificate for this. 568 569 Further we create a key used to sign JWTs for users running `choria login`, it needs a special JWT: 570 571 ```nohighlight 572 [choria@issuer ~]$ mkdir -p development/aaasvc 573 [choria@issuer ~]$ choria jwt client development/aaasvc/chain-signer.jwt aaa_chain_signer \ 574 development/issuer/private.key \ 575 --public-key 17807f2c5fa959383ee5851813863426525c081f6464556e5dec482e815caded \ 576 --no-fleet-management \ 577 --issuer \ 578 --validity 365d 579 Saved token to client development/aaasvc/chain-signer.jwt, use 'choria jwt view client development/aaasvc/chain-signer.jwt' to view it 580 ``` 581 582 Copy this file to the temporary AAA container above as `/etc/aaasvc/chain-signer.jwt`. 583 584 Next we create the credentials that will sign every RPC request: 585 586 ```nohighlight 587 host$ docker run -ti --rm -v `pwd`/aaasvc:/etc/aaasvc --user root --entrypoint bash registry.choria.io/choria-nightly/choria:nightly -l 588 [root@38f75c90e475 /]# choria jwt keys /etc/aaasvc/request-signer-private.key /etc/aaasvc/reqeuest-signer-public.key 589 Public Key: 535e9d337e555b9bf9079269567b8d9cb812fdf54797e5d5441ed778f1db68d8 590 591 Ed25519 seed saved in /etc/aaasvc/request-signer-private.key 592 ``` 593 594 This is the key used to sign individual user RPC requests on their behalf, it needs a special JWT: 595 596 ```nohighlight 597 [choria@issuer ~]$ mkdir -p development/aaasvc 598 [choria@issuer ~]$ choria jwt client development/aaasvc/request-signer.jwt aaa_request_signer \ 599 development/issuer/private.key \ 600 --public-key 535e9d337e555b9bf9079269567b8d9cb812fdf54797e5d5441ed778f1db68d8 \ 601 --no-fleet-management \ 602 --auth-delegation \ 603 --validity 365d 604 Saved token to development/aaasvc/request-signer.jwt, use 'choria jwt view development/aaasvc/request-signer.jwt' to view it 605 ``` 606 607 Place it in `/etc/aaasvc/request-signer.jwt` on the AAA Service container above. 608 609 Finally, we need to create the credentials that allow the request signer to run as a Choria Service. 610 611 ```nohighlight 612 host$ docker run -ti --rm -v `pwd`/aaasvc:/etc/aaasvc --user root --entrypoint bash registry.choria.io/choria-nightly/choria:nightly -l 613 [root@38f75c90e475 /]# choria jwt keys /etc/aaasvc/signer-service-private.key /etc/aaasvc/signer-service-public.key 614 Public Key: c5c1323f66bb8324d019249e3476d9f11f9deb70efa60255593dde30ef3b8a01 615 616 Ed25519 seed saved in /etc/aaasvc/signer-service-private.key 617 ``` 618 619 Let's create the `server` JWT that will host the RPC Service for signing requests 620 ```nohighlight 621 [choria@issuer ~]$ choria jwt server development/aaasvc/signer-service.jwt \ 622 aaa.example.net \ 623 c5c1323f66bb8324d019249e3476d9f11f9deb70efa60255593dde30ef3b8a01 \ 624 development/issuer/private.key \ 625 --org choria \ 626 --collectives choria \ 627 --service \ 628 --validity 365d 629 ``` 630 631 Place it in `/etc/aaasvc/signer-service.jwt` on the AAA Service container above. 632 633 We can now configure the various parts of the AAA Service, it needs a `/etc/aaasvc/choria.conf` to connect to the network with: 634 635 ```ini 636 identity = aaa.example.net 637 plugin.security.provider = choria 638 plugin.security.choria.seed_file = /etc/aaasvc/signer-service-private.key 639 plugin.security.choria.token_file = /etc/aaasvc/signer-service.jwt 640 plugin.choria.middleware_hosts = broker.example.net:4222 641 ``` 642 643 We need an `/etc/aaasvc/aaasvc.conf`: 644 645 {{% notice tip %}} 646 See the [User List Authenticator docs](https://choria-io.github.io/aaasvc/configuration/userlist/index.html) about user, passwords and more. The passwords below are all `secret`. 647 {{% /notice %}} 648 649 ```json 650 { 651 "choria_config": "/etc/aaasvc/choria.conf", 652 "logfile": "/dev/stdout", 653 "loglevel": "info", 654 "authenticator": "userlist", 655 "authorizer": "opa", 656 "signer": "basicjwt", 657 "monitor_port": 8081, 658 "site": "PREVIEW", 659 "tls_certificate": "/etc/aaasvc/https-public.crt", 660 "tls_key":"/etc/aaasvc/https-private.key", 661 "port":8080, 662 "basicjwt_signer": { 663 "signing_certificate": "/etc/aaasvc/chain-signer-public.key", 664 "signing_token": "/etc/aaasvc/request-signer.jwt", 665 "signing_seed": "/etc/aaasvc/request-signer-private.key", 666 "max_validity":"2h", 667 "choria_service": true 668 }, 669 "userlist_authenticator": { 670 "signing_key": "/etc/aaasvc/chain-signer-private.key", 671 "signing_token": "/etc/aaasvc/chain-signer.jwt", 672 "validity": "1h", 673 "users": [ 674 { 675 "username": "admin", 676 "password": "$2a$05$zQIl4gUZbqmKhpQhIeWx3uDWhAZaHoG34zW1ZsxXQt5xpL5f4uyny", 677 "opa_policy_file": "/etc/aaasvc/admin.rego", 678 "broker_permissions": { 679 "org_admin": true, 680 "system_user": true, 681 "signed_fleet_management": true 682 } 683 }, 684 { 685 "username": "streams", 686 "password": "$2a$05$zQIl4gUZbqmKhpQhIeWx3uDWhAZaHoG34zW1ZsxXQt5xpL5f4uyny", 687 "broker_permissions": { 688 "streams_admin": true 689 } 690 }, 691 { 692 "username": "choria", 693 "password": "$2a$05$zQIl4gUZbqmKhpQhIeWx3uDWhAZaHoG34zW1ZsxXQt5xpL5f4uyny", 694 "opa_policy_file": "/etc/aaasvc/admin.rego", 695 "broker_permissions": { 696 "signed_fleet_management": true 697 } 698 } 699 ] 700 } 701 } 702 ``` 703 704 {{% notice warning %}} 705 See the [AAA Service Docs](https://choria-io.github.io/aaasvc/configuration/opa/) for writing real policies. 706 {{% /notice %}} 707 708 Finally, we create an Open Policy Agent policy for the Choria Users, this one just allows everything. Place it in `/etc/aaasvc/admin.rego` 709 710 ```opa 711 package io.choria.aaasvc 712 713 default allow = true 714 ``` 715 716 We can now start the AAA Service: 717 718 ```nohightlight 719 host$ sudo chown -R 2048:2048 aaasvc 720 host$ docker run -ti --rm \ 721 -v `pwd`/aaasvc:/etc/aaasvc \ 722 --network choria_v2proto \ 723 --hostname aaa.example.net \ 724 choria/aaasvc:nightly run --config /etc/aaasvc/aaasvc.conf 725 ``` 726 727 #### Client 728 729 Choria Clients will now enroll using `choria login` which will issue them a 1-hour valid JWT with their policies and more embedded. 730 731 Lets create a new client container: 732 733 ```nohighlight 734 $ docker run -ti --rm --entrypoint bash \ 735 --network choria_v2proto \ 736 --hostname client.example.net \ 737 registry.choria.io/choria-nightly/choria:nightly -l 738 ``` 739 740 We create a system-wide client configuration in `/etc/choria/client.conf`: 741 742 ```nohighlight 743 loglevel = warn 744 plugin.choria.middleware_hosts = broker.example.net:4222 745 plugin.choria.network.system.user = system 746 plugin.choria.network.system.password = sYst3m 747 748 plugin.security.provider = choria 749 plugin.security.choria.token_file = ~/.config/choria/client.jwt 750 plugin.security.choria.seed_file = ~/.config/choria/client.key 751 plugin.choria.security.request_signer.service = true 752 plugin.login.aaasvc.login.url = https://aaa.example.net:8080/choria/v1/login 753 ``` 754 755 Users can now run `choria login` and authenticate using on of the usernames and the password `secret`. 756 757 The client is now entirely self-service, the token expires every hour, and they just run `choria login` again. They can do this on as many machines as they have and admins do not get involved. 758 759 Every RPC request the client makes will be signed by the AAA Service after Authorization against the OPA Policy and auditing the outcome. Servers will also validate the OPA policy before executing anything. 760 761 ### Vault as Organization Issuer 762 763 We support using [Hashicorp Vault](https://www.vaultproject.io/) as the Organization Issuer. In that mode the Private Key is created inside Vault and never has to leave Vault at all. 764 765 {{% notice warning %}} 766 Here we'll show the developer mode Vault you should use a Production deployment of Vault and not a simple developer local build. 767 {{% /notice %}} 768 769 #### Starting Vault 770 771 We start Vault in dev mode with a static secret defined: 772 773 ```nohighlight 774 $ vault server -dev -dev-root-token-id root 775 ==> Vault server configuration: 776 777 Api Address: http://127.0.0.1:8200 778 Cgo: disabled 779 Cluster Address: https://127.0.0.1:8201 780 .... 781 WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory 782 and starts unsealed with a single unseal key. The root token is already 783 authenticated to the CLI, so you can immediately begin using Vault. 784 785 You may need to set the following environment variables: 786 787 $ export VAULT_ADDR='http://127.0.0.1:8200' 788 789 The unseal key and root token are displayed below in case you want to 790 seal/unseal the Vault or re-authenticate. 791 792 Unseal Key: JKyF70iBqv3d9rreY9rhY0/EQ9ornriTZHV+kVWpJ+w= 793 Root Token: root 794 795 Development mode should NOT be used in production installations! 796 ``` 797 798 #### Configuring the Transit Secrets Engine 799 800 Choria relies on the [Transit Secrets Engine](https://developer.hashicorp.com/vault/docs/secrets/transit) to offload signing of keys. 801 802 Let's enable that: 803 804 ```nohighlight 805 $ export VAULT_ADDR='http://127.0.0.1:8200 806 $ export VAULT_TOKEN='root' 807 $ vault secrets enable transit 808 Success! Enabled the transit secrets engine at: transit/ 809 ``` 810 811 #### Create the Issuer 812 813 We use the Vault API to create an `ed25519` key stored in Vault storage: 814 815 ```nohighlight 816 $ export VAULT_ADDR='http://127.0.0.1:8200 817 $ export VAULT_TOKEN='root' 818 $ vault write transit/keys/choria_issuer type=ed25519 819 Success! Data written to: transit/keys/choria_issuer 820 $ vault read transit/keys/choria_issuer 821 Key Value 822 --- ----- 823 allow_plaintext_backup false 824 auto_rotate_period 0s 825 deletion_allowed false 826 derived false 827 exportable false 828 imported_key false 829 keys map[1:map[creation_time:2022-12-12T11:21:23.248802439+01:00 name:ed25519 public_key:IZu6TyYAwWeuyD3Q0tEiCGbYBjkRjoOcWO/OI9PDmOE=]] 830 latest_version 1 831 min_available_version 0 832 min_decryption_version 1 833 min_encryption_version 0 834 name choria_issuer 835 supports_decryption false 836 supports_derivation true 837 supports_encryption false 838 supports_signing true 839 type ed25519 840 ``` 841 842 Here we see that the public key is shown as `public_key:IZu6TyYAwWeuyD3Q0tEiCGbYBjkRjoOcWO/OI9PDmOE=`, lets turn that into a hex encoded string: 843 844 {{% notice tip %}} 845 If saving the public key to a file ensure there is no trailing new line 846 {{% /notice %}} 847 848 ```nohighlight 849 $ echo IZu6TyYAwWeuyD3Q0tEiCGbYBjkRjoOcWO/OI9PDmOE=|base64 -d|xxd -p -c 64 850 219bba4f2600c167aec83dd0d2d1220866d80639118e839c58efce23d3c398e1 851 ``` 852 853 This is the Organization Issuer you configure in your broker and elsewhere. 854 855 #### Signing JWTs using Vault 856 857 The `choria jwt` commands support the `--vault` flag that requires `VAULT_ADDR` and `VAULT_TOKEN` to be set in environment. 858 859 ```nohighlight 860 $ choria jwt server \ 861 token.jwt \ 862 server.example.net \ 863 3f2d5d01f3c5caa0cd7359512c7e2d9a727fa0392f47f50adee1866bf02cbe12 \ 864 choria_issuer \ 865 --collectives=choria \ 866 --vault 867 ``` 868 869 Here we pass `--vault` and instead of a path to the Issuer Private Key we give the name `choria_issuer` that we created in Vault.