diff --git a/SUMMARY.md b/SUMMARY.md index e5e30a1..5590927 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -15,6 +15,9 @@ * [Tokens](nats_server/tokens.md) * [Username/Password](nats_server/username_password.md) * [TLS Authentication](nats_server/tls_mutual_auth.md) + * [NKeys](nats_server/nkey_auth.md) + * [Accounts](nats_server/jwt_auth.md) + * [Authentication Timeout](nats_server/auth_timeout.md) * [Authorization](nats_server/authorization.md) * [Clustering](nats_server/clustering.md) * [TLS Authentication](nats_server/cluster_tls.md) @@ -28,6 +31,12 @@ ### NATS Tools * [mkpasswd](nats_tools/mkpasswd.md) +* [nk](nats_tools/nk.md) +* [nsc](nats_tools/nsc/README.md) + * [Basics](nats_tools/nsc/nsc.md) + * [Streams](nats_tools/nsc/streams.md) + * [Services](nats_tools/nsc/services.md) + * [Signing Keys](nats_tools/nsc/signing_keys.md) * [NATS Top](nats_tools/nats_top/README.md) * [Tutorial](nats_tools/nats_top/tutorial.md) * [Benchmarking](nats_tools/natsbench.md) diff --git a/nats_server/auth_timeout.md b/nats_server/auth_timeout.md new file mode 100644 index 0000000..04e8de2 --- /dev/null +++ b/nats_server/auth_timeout.md @@ -0,0 +1,19 @@ +# Authentication Timeout + +Much like the [`tls timeout` option](/nats_server/tls.md#tls-timeout), authentication can specify a `timeout` value. + +If a client doesn't authenticate to the server within the specified time, the server disconnects the server to prevent abuses. + +Timeouts are specified in seconds (and can be fractional). + +As with TLS timeouts, long timeouts can be an opportunity for abuse. If setting the authentication timeout, it is important to note. that it should be longer than the `tls timeout` option, as the authentication timeout includes the TLS upgrade time. + +``` +authorization: { + timeout: 3 + users: [ + {user: a, password b}, + {user: b, password a} + ] +} +``` \ No newline at end of file diff --git a/nats_server/jwt_auth.md b/nats_server/jwt_auth.md new file mode 100644 index 0000000..15dc1f7 --- /dev/null +++ b/nats_server/jwt_auth.md @@ -0,0 +1,93 @@ + +## Accounts + +_Accounts_ expand on the [NKEY](nkey_auth.md) authentication foundation and recasts client authentication and authorization from a server configuration to a distributed and delegated authentication and authorization model. + +With other authentication mechanisms, all clients can publish and subscribe to anything unless explicitly configured otherwise. To protect clients and information, you have to carefully carve the subject space and permission clients. + +Each account is *isolated* from other accounts, thus enabling *multi-tenancy* in the server. With accounts, the subject space is not globally shared, greatly simplifying the messaging environment. Instead of devising complicated subject name carving patterns, clients can use short subjects without explicit authorization rules. + +Messaging exchange between different accounts is enabled by _exporting_ streams and services from one account and _importing_ them into another. When an export is sensitive, the export can require a constraint that authorization for its import be provided. Thus it is easy to audit exporters and limit importers. Best of all, each account is in full control of what is exported or imported and from whom. + +> While the name _account_ implies one or more users, it is much simpler and enlightening to think of one account as a messaging container for one application. Users in the account are simply the minimum number of services that must work together to provide some functionality. +> In simpler terms, more accounts with few (even one) clients is a better design topology than a large account with many users with complex authorization configuration. + +Accounts leverage [JSON Web Tokens (JWT)](https://jwt.io/) to describe the various entities supported. When a client connects, servers query for account JWTs and validate a trust chain. Users are not directly tracked by the server, but rather verified as belonging to an account. This enables the managing of users without requiring server configuration updates. + +Effectively, Accounts provide for a distributed configuration paradigm. Previously each user (or client) needed to be known and authorized a priori in the server’s configuration requiring an administrator to modify and update server configurations. Accounts eliminate these chores. + + +### JSON Web Tokens + +[JSON Web Tokens (JWT)](https://jwt.io/) are an open and industry standard [RFC7519](https://tools.ietf.org/html/rfc7519) method for representing claims securely between two parties. + +Claims are a fancy way of asserting information on a _subject_. In this context, a _subject_ is the entity being described (not a messaging subject). Standard JWT claims are typically digitally signed and verified. + +NATS further restricts JWTs by requiring that JWTs be: + +- Digitally signed _always_ and only using [Ed25519](https://ed25519.cr.yp.to/). +- NATS adopts the convention that all _Issuer_ and _Subject_ fields in a JWT claim must be a public [NKEY](nkey_auth.md). +- It also introduces type requirements into claims, enabling the pairing of specific roles matching those supported by [NKeys](https://github.com/nats-io/nkey). + +#### NKey Roles + +NKey Roles are: + +- Operators +- Accounts +- Users + +Roles are hierarchical and form a chain of trust. Operators issue Accounts which in turn issue Users. Servers trust specific Operators. If an account is issued by an operator that is trusted, account users are trusted. + + +### The Authentication Process + +When a _User_ connects to a server, it presents a JWT issued by its _Account_. The user proves its identity by signing a server-issued cryptographic challenge with its private key. The signature verification validates that the signature is attributable to the user's public key. Next, the server retrieves the associated account JWT that issued the user. It verifies the _User_ issuer matches the referenced account. Finally, the server checks that a trusted _Operator_ issued the _Account_, completing the trust chain verification. + + +### The Authorization Process + +From an authorization point of view, the Account provides information on messaging subjects that are imported from other accounts (including any ancillary related authorization) as well as messaging subjects exported to other accounts. Accounts can also bear limits, such as the maximum number of connections they may have. A user JWT can express restrictions on the messaging subjects that it can publish or subscribe to. + +When a new user is added to an account, the account configuration need not change, as each user can and should have its own user JWT that can be verified by simply resolving its parent account. + +### JWTs and Privacy + +One crucial detail to keep in mind is that while in other systems JWTs are used as sessions or proof of authentication, NATS JWTs are only used as configuration describing: + +- the public ID of the entity +- the public ID of the entity that issued it +- capabilities of the entity + +Authentication is a public key cryptographic process — a client signs a nonce proving identity while the trust chain and configuration provides the authorization. + +The server is never aware of private keys but can verify that a signer or issuer indeed matches a specified or known public key. + +Lastly, all NATS JWTs (Operators, Accounts, Users and others) are expected to be signed using the [Ed25519](https://ed25519.cr.yp.to/) algorithm. If they are not, they are rejected by the system. + +### Sharing Between Accounts + +While accounts provide isolation, there are many cases where you want to be able to consume messages produced by one account in another. There are two kinds of shares that an account can _export_: + +- Streams +- Services + +Streams are messages published by a foreign account; Subscribers in an _importing_ account can receive messages from a stream _exported_ by another. + +Services are endpoints exported by a foreign account; Requesters _importing_ the service can publish requests to the _exported_ endpoint. + +Streams and Services can be public; Public exports can be imported by any account. Or they can be private. Private streams and services require an authorization token from the exporting account that authorizes the foreign account to import the stream or service. + +An importing account can remap the subject where a stream subscriber will receive messages or where a service requestor can make requests. This enables the importing account to simplify their subject space. + +Exports and imports from an account are explicit, and they are visible in the account's JWT. For private exports, the import will embed an authorization token or a URL storing the token. Imports and exports make it easy to audit where data is coming or going. + +### Configuration + +Entity JWT configuration is done using the [`nsc` tool](/nats_tools/nsc/README.md). The basic steps include: + +- [Creation of an operator JWT](/nats_tools/nsc/nsc.md#creating-an-operator) +- [Configuring an Account Server](/nats_tools/nsc/nsc.md#account-server-configuration) +- [Setting up the NATS server to resolve Accounts](/nats_tools/nsc/nsc.md#nats-server-configuration) + +After that, `nsc` is used to create and edit accounts and users. \ No newline at end of file diff --git a/nats_server/nkey_auth.md b/nats_server/nkey_auth.md index c937482..13dd25c 100644 --- a/nats_server/nkey_auth.md +++ b/nats_server/nkey_auth.md @@ -1,18 +1,13 @@ ## NKey Authentication -NATS 2.0 introduces a new highly secure public-key signature system based on [Ed25519](https://ed25519.cr.yp.to/). +NKeys are a new highly secure public-key signature system based on [Ed25519](https://ed25519.cr.yp.to/). With NKeys the server can verify identities without ever storing secrets on the server. The authentication system works by requiring a connecting client to provide its public key and digitally sign a challenge with its private key. The server generates a random challenge with every connection request, making it immune to playback attacks. The generated signature is validated against the provided public key, thus proving the identity of the client. If the public key is known to the server, authentication succeeds. > NKey is an awesome replacement for token authentication, because a connecting client will have to prove it controls the private key for the authorized public key. -### Installing nk +To generate nkeys, you'll need the [`nk` tool](/nats_tools/nk.md). -To get started with NKeys, you’ll need the `nk` tool from https://github.com/nats-io/nkeys/nk repository. If you have _go_ installed, enter the following at a command prompt: - -```bash -> go get github.com/nats-io/nk -``` ### Generating NKeys and Configuring the Server @@ -26,9 +21,9 @@ UDXU4RCSJNZOIQHZNWXHXORDPRTGNJAHAHFRGZNEEJCPQTT2M7NLCNF4 The first output line starts with the letter `S` for _Seed_. The second letter `U` stands for _User_. Seeds are private keys; you should treat them as secrets and guard them with care. -The second line starts with the letter `U` for _User_, and is a public key which can be safely shared. +The second line starts with the letter `U` for _User_, and is a public key which can be safely shared. -To use nkey authentication add a user and set the `nkey` property to the public key of the user you want to authenticate: +To use nkey authentication, add a user and set the `nkey` property to the public key of the user you want to authenticate: ```text authorization: { diff --git a/nats_server/tls_mutual_auth.md b/nats_server/tls_mutual_auth.md index d22a643..3bf6c31 100644 --- a/nats_server/tls_mutual_auth.md +++ b/nats_server/tls_mutual_auth.md @@ -89,4 +89,8 @@ authorization { {user: "CN=example.com,OU=CNCF"} ] } -``` \ No newline at end of file +``` + +## TLS Timeout + +[TLS timeout](/nats_server/tls.md#tls-timeout) is described here. \ No newline at end of file diff --git a/nats_tools/nsc/README.md b/nats_tools/nsc/README.md new file mode 100644 index 0000000..358d357 --- /dev/null +++ b/nats_tools/nsc/README.md @@ -0,0 +1,41 @@ +## NATS Account Configuration + +NATS account configurations are built using the `nsc` tool. The NSC tool allows you to: + +- Create and Edit Operators, Accounts, Users +- Manage subscribe and publish permissions for Users +- Add and delete Exports and Imports +- Generate Activations +- Generate User credential files +- Describe Operators, Accounts, Users, and Activations + +## Installation + +Installing `nsc` is easy: + +```text +curl -L https://raw.githubusercontent.com/nats-io/nsc/master/install.py | python +``` + +The script will download the latest version of `nsc` and install it into your system. + +## Tutorials + +You can find various task-oriented tutorials to working with the tool here: + +- [Basic Usage](nsc.md) +- [Configuring Streams](streams.md) +- [Configuring Services](services.md) +- [Signing Keys](signing_keys.md) + +## Tool Documentation + +For more specific browsing of the tool syntax, check out the `nsc` tool documentation. +It can be found within the tool itself: + +```text +> nsc help +``` + +Or an online version [here](https://nats-io.github.io/nsc) + diff --git a/nats_tools/nsc/nsc.md b/nats_tools/nsc/nsc.md new file mode 100644 index 0000000..69570f9 --- /dev/null +++ b/nats_tools/nsc/nsc.md @@ -0,0 +1,240 @@ +## NSC + +NATS uses JWTs to armor the various identity and authorization artifacts. These JWTs are created with the `nsc` tool. NSC simplifies the tasks of creating and managing identities and other JWT artifacts. + +There’s a logical hierarchy to the entities: + +- `Operators` are responsible for running nats-servers, and signing account JWTs that set the limits on what an account can do, such as the number of connections, data limits, etc. + +- `Accounts` are responsible for issuing user JWTs, and for declaring what subjects can be exported to other accounts, and what subjects they import from other accounts and what the local subjects for those imports are. + +- `Users` are issued by an account, and encode limits regarding usage and authorization over the subject space. + +NSC allows you to create, edit, delete these entities, and will be central to all account based configuration. + +In this guide, you’ll run end-to-end on some of the configuration scenarios: + +- generate JWTs +- make JWTs accessible to a nats-server +- configure a nats-server to use JWTs + +Let’s run through the process of creating some identities and JWTs and work through the process. + +By default JWTs are written to `~/.nsc` and secrets to `~/.nkeys`. You can easily change those locations by setting `NSC_HOME` and `NKEYS_PATH` respectively in your environment to your desired locations. + +> The $NKEYS_PATH stores secrets. Since nkeys relies on cryptographic signatures to prove identity, anyone with access to your private keys will be able to assume your identity. With that said, treat them as secrets and guard them carefully. + +Let’s see what settings `nsc` has in its environment: + +```text +> nsc env +╭──────────────────────────────────────────╮ +│ NSC Environment │ +├──────────────────┬─────┬─────────────────┤ +│ Setting │ Set │ Effective Value │ +├──────────────────┼─────┼─────────────────┤ +│ $NKEYS_PATH │ No │ ~/.nkeys │ +│ $NSC_HOME │ No │ ~/.nsc │ +│ Config │ │ ~/.nsc/nsc.json │ +├──────────────────┼─────┼─────────────────┤ +│ Stores Dir │ │ ~/.nsc/nats │ +│ Default Operator │ │ │ +│ Default Account │ │ │ +│ Default Cluster │ │ │ +╰──────────────────┴─────┴─────────────────╯ +``` + +By default you’ll see that generated secrets are stored in `~/.nkeys`, and configurations in `~/.nsc/nats`. All operations are assumed to be in a context of the current operator and current account. When working with multiple operators and accounts you may need to set the current one. You can easily do so by issuing the `nsc env` and provide flags to set the current operator or account. See `nsc env —help` for more details. + + +#### Creating an Operator + +Let’s create an operator called `Test`: + +```text +> nsc add operator -n Test +Generated operator key - private key stored “~/.nkeys/Test/Test.nk” +Success! - added operator "Test" +``` + +With the above incantation, the tool generated an NKEY for the operator, stored the private key safely in `~/.nkeys/Test/Test.nk`. The file contains a single line, with the seed value for the NKEY. + + > You can tell the key is a seed if it starts with the letter `S`. The type of the key is will be the second letter an `O`, `A` or `U` for _Operator_, _Account_ or _User_. If the key does not start with an `S` you have instead a public key. + +The tool also created a JWT with all default settings for the operator test, and stored it in `~/.nsc/nats/Test/Test.jwt`. The `~/.nsc/nats/Test` directory will also contain a directory where accounts related to this operator will live. + +You can view the JWT by entering the command: + +```text +> nsc describe operator +╭───────────────────────────────────────╮ +│ Operator Details │ +├─────────────┬─────────────────────────┤ +│ Name │ Test │ +│ Operator ID │ OAYI3YUZSWDN │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-24 19:48:55 UTC │ +│ Expires │ │ +╰─────────────┴─────────────────────────╯ +``` + +Note that the Operator ID is truncated to simplify the output, to get the full ID, do: + +```text +> nsc describe operator -W +╭────────────────────────────────────────────────────────────────────────╮ +│ Operator Details │ +├─────────────┬──────────────────────────────────────────────────────────┤ +│ Name │ Test │ +│ Operator ID │ OAYI3YUZSWDNMERD2IN3HZSIP3JA2E3VDTXSTEVOIII273XL2NABJP64 │ +│ Issuer ID │ OAYI3YUZSWDNMERD2IN3HZSIP3JA2E3VDTXSTEVOIII273XL2NABJP64 │ +│ Issued │ 2019-04-24 19:48:55 UTC │ +│ Expires │ │ +╰─────────────┴──────────────────────────────────────────────────────────╯ +``` + +With an operator, we are ready to create our first account. + +#### Creating an Account + +Let’s create an account called `TestAccount`: + +``` +> nsc add account -n TestAccount +Generated account key - private key stored “~/.nkeys/Test/accounts/TestAccount/TestAccount.nk" +Success! - added account "TestAccount" +``` + +As we did with the operator, we can describe the account: + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ TestAccount │ +│ Account ID │ AC7PO3MREV26 │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-24 19:58:01 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +│ Exports │ None │ +╰──────────────────────────┴─────────────────────────╯ +``` + +Again, specifying the `-W` flag will print the complete account ID (the public key identifying the account). + +Note that the issuer for the account is the ID for the operator (the public key identifying the operator). + +Now we are ready to add a user. + +#### Creating a User + +Let’s add a user named ‘TestUser’: + +```text +> nsc add user -n TestUser +Generated user key - private key stored "~/.nkeys/Test/accounts/TestAccount/users/TestUser.nk" +Generated user creds file "~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds" +Success! - added user "TestUser" to "TestAccount" +``` + +Note that when we added the user, we got a message telling us about a `.creds` file being created. The `.creds` file contains the JWT describing the user, and the private (seed) key for the user. This file is formatted in a special way for use by nats client libraries. Client libraries can extract the JWT and seed key, and connect to a server expecting JWT authentication, provide the JWT and use the private key to sign the nonce to verify its identity. + +And let’s describe it: + +```text +> nsc describe user +╭───────────────────────────────────────────╮ +│ User │ +├─────────────────┬─────────────────────────┤ +│ Name │ TestUser │ +│ User ID │ UCQB7NONBKRC │ +│ Issuer ID │ AC7PO3MREV26 │ +│ Issued │ 2019-04-24 20:36:25 UTC │ +│ Expires │ │ +├─────────────────┼─────────────────────────┤ +│ Max Messages │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Network Src │ Any │ +│ Time │ Any │ +╰─────────────────┴─────────────────────────╯ +``` + +Let’s put all of this together, and create a simple server configuration that accepts sessions from TestUser. + +### Account Server Configuration + +To configure a server to use accounts you need an _account resolver_. An account resolver exposes a URL where a nats-server can query for JWTs belonging to an account. + +A simple built-in resolver is the `MEMORY` resolver which simply statically maps account public keys to an account JWT in the server’s configuration file. It is somewhat easier to configure because it doesn’t require another moving part, but fails provide the needed experience of setting up an account server. Let’s setup an _Account Server_. + +Installing the Account Server + +```text +> go get github.com/nats-io/nats-account-server +``` + +The account server has options to enable you to use an nsc directory directly. Let’s start one: + +```text +> nats-account-server -nsc ~/.nsc/nats/Test +``` + +Above we pointed the account server to our nsc data directory (more specifically to the `Test` operator that we created earlier). By default, the server listens on the localhost at port 9090. + +We are now ready to configure the nats-server + +### NATS Server Configuration + +If you don’t have a nats-server installed, let’s do that now: + +```text +> go get github.com/nats-io/nats-server +``` + +Let’s create a configuration that references our operator JWT and the nats-account-server as a resolver: + +```yaml +operator: /Users/synadia/.nsc/nats/Test/Test.jwt +resolver: URL(http://localhost:9090/jwt/v1/accounts/) +``` + +At minimum the server requires the `operator` JWT, which we have pointed at directly, and a resolver. The resolver has two types `MEM` and `URL`. We are interested in the `URL` since we want the nats-server to talk to the account server. Note we put the URL of the server with the path `/jwt/v1/accounts`. Currently this is where the account server expects requests for account information. + +### Client Testing + +Let’s install some tooling: + +```text +> go get github.com/nats-io/go-nats/examples/nats-pub + +> go get github.com/nats-io/go-nats/examples/nats-sub +``` + +Create a subscriber: +```text +nats-sub -creds ~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds ">" +Listening on [>] +``` + +Publish a message: +```text +nats-pub -creds ~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds hello NATS +Published [hello] : 'NATS' +``` + +Subscriber shows: +```text +[#1] Received on [hello]: ’NATS’ +``` + diff --git a/nats_tools/nsc/services.md b/nats_tools/nsc/services.md new file mode 100644 index 0000000..13b0528 --- /dev/null +++ b/nats_tools/nsc/services.md @@ -0,0 +1,293 @@ +## Services + +To share services that other accounts can reach via request reply, you have to _Export_ a _Service_. _Services_ are associated with the account performing the replies and are advertised in the exporting accounts' JWT. + +### Adding a Public Service Export + +To add a service to your account: + +```text +> nsc add export --name "srv" --subject "help" --service +Success! - added public service export "srv" +``` + +To review the service export: + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ TestAccount │ +│ Account ID │ AC7PO3MREV26 │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-29 14:20:13 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭───────────────────────────────────╮ +│ Exports │ +├──────┬─────────┬─────────┬────────┤ +│ Name │ Type │ Subject │ Public │ +├──────┼─────────┼─────────┼────────┤ +│ help │ Service │ help │ Yes │ +╰──────┴─────────┴─────────┴────────╯ +``` + +### Importing a Service + +Importing a service enables you to send requests to the remote _Account_. To import a Service, you have to create an _Import_. To create an import you need to know: + +- The exporting account’s public key +- The subject the service is listening on +- You can map the service’s subject to a different subject +- Self-imports are not valid; you can only import services from other accounts. + +To learn how to inspect a JWT from an account server, [check this article](../nas/inspecting_jwts.md). + + +```text +> nsc add import --src-account AC7PO3MREV26U3LFZFP5BN3HAI32X3PKLBRVMPAETLEHWPQEUG7EJY4H --remote-subject help --service +Success! - added service import "help" +``` + +Verifying our handywork: + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ AccountB │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-29 14:37:49 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Exports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭─────────────────────────────────────────────────────────────────────────╮ +│ Imports │ +├─────────┬─────────┬─────────┬─────────┬─────────┬──────────────┬────────┤ +│ Name │ Type │ Remote │ Local │ Expires │ From Account │ Public │ +├─────────┼─────────┼─────────┼─────────┼─────────┼──────────────┼────────┤ +│ help │ Service │ help │ help │ │ AC7PO3MREV26 │ Yes │ +╰─────────┴─────────┴─────────┴─────────┴─────────┴──────────────┴────────╯ +``` + +### Testing the Service + +To test the service, we can install the `nats-req` and `nats-rply` tools: + +Set up a process to handle the request: +```text +> go get github.com/nats-io/go-nats/examples/nats-rply + +> nats-rply -creds ~/.nkeys/Test/accounts/AccountB/users/userb.creds "help" "I will help" +Listening on [help] +``` + +Send the request: +```text +> go get github.com/nats-io/go-nats/examples/nats-req +> nats-req -creds ~/.nkeys/Test/accounts/AccountB/users/userb.creds help me +Published [help] : 'me' +``` + +The service receives the request: +```text +[#1] Received on [help]: 'me' +``` + +And the response is received by the requestor: +```text +Received [_INBOX.v6KAX0v1bu87k49hbg3dgn.StIGJF0D] : 'I will help' +``` + + +## Securing Services + +If you want to create a service that is only accessible to accounts you designate you can create a _private_ service. The export will be visible in your account, but subscribing accounts will require an authorization token that must be created by you and generated specifically for the requesting account. + +Let’s create an account and user for our stream client: +```text +> nsc add account --name AccountB +Generated account key - private key stored “~/.nkeys/Test/accounts/AccountB/AccountB" +Success! - added account "AccountB" + +> nsc add user --name userb +Generated user key - private key stored "~/.nkeys/Test/accounts/AccountB/users/userb” +Generated user creds file "~/.nkeys/Test/accounts/AccountB/users/userb.creds" +Success! - added user “userb” to “AccountB” +``` + +The authorization token is simply a JWT signed by your account where you authorize the client account to import your service. + +### Creating a Private Service Export + +```text +> nsc add export --name phelp --subject "help.>" --private --service +Success! - added private service export "phelp" +``` + +As before, we declared an export, but this time we added the `--private` flag. The other thing to note is that the subject for the request has a wildcard. This enables the account to map specific subjects to specifically authorized accounts. + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ TestAccount │ +│ Account ID │ AC7PO3MREV26 │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-29 14:59:42 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭────────────────────────────────────╮ +│ Exports │ +├───────┬─────────┬─────────┬────────┤ +│ Name │ Type │ Subject │ Public │ +├───────┼─────────┼─────────┼────────┤ +│ phelp │ Service │ help.> │ No │ +╰───────┴─────────┴─────────┴────────╯ +``` + +### Generating an Activation Token + +For the foreign account to _import_ a private service and be able to send requests, you have to generate an activation token. The activation token in addition to granting permission to the account allows you to subset the service’s subject: + +To generate a token, you’ll need to know the public key of the account importing the service. + +```text +> nsc generate activation -o /tmp/activation.jwt --target-account AAL5Q2B3SMSO5AS3APJFUNAIKUCEQJPAQ76XEBTVOCQCXXGKP3YMGGN4 --subject "help.AAL5Q2B3SM" --service +generated "phelp" activation for account "AAL5Q2B3SMSO5AS3APJFUNAIKUCEQJPAQ76XEBTVOCQCXXGKP3YMGGN4". +JTI is "IY4ZUWLNLOTO5N5UDLOFEBCOMHB6MKQMK4ZELA2BSPKMXSEARXOA" +``` + +In the above invocation, we generated an activation redirecting the output to `/tmp/activation.jwt`. The activation only allows the client account to perform requests on `help.AAL5Q2B3SM`. + +For completeness, the contents of the JWT file looks like this: + +```text +> cat /tmp/activation.jwt +-----BEGIN NATS ACTIVATION JWT----- +eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJJWTRaVVdMTkxPVE81TjVVRExPRkVCQ09NSEI2TUtRTUs0WkVMQTJCU1BLTVhTRUFSWE9BIiwiaWF0IjoxNTU2NTUwMDczLCJpc3MiOiJBQzdQTzNNUkVWMjZVM0xGWkZQNUJOM0hBSTMyWDNQS0xCUlZNUEFFVExFSFdQUUVVRzdFSlk0SCIsIm5hbWUiOiJoZWxwLkFBTDVRMkIzU00iLCJzdWIiOiJBQUw1UTJCM1NNU081QVMzQVBKRlVOQUlLVUNFUUpQQVE3NlhFQlRWT0NRQ1hYR0tQM1lNR0dONCIsInR5cGUiOiJhY3RpdmF0aW9uIiwibmF0cyI6eyJzdWJqZWN0IjoiaGVscC5BQUw1UTJCM1NNIiwidHlwZSI6InNlcnZpY2UifX0.VFYHPA0e67RFR-XFy7Q7pS90hzZvP5k3OsldjaDrIXP4UdpuQeUhv9qK9EMK40pcgH6NzJ7gmaZLU6RpAcbXAg +------END NATS ACTIVATION JWT------ +``` + +When decoded it looks like this: + +```text +> nsc describe jwt -f /tmp/activation.jwt +╭───────────────────────────────────────────╮ +│ Activation │ +├─────────────────┬─────────────────────────┤ +│ Import Type │ Service │ +│ Import Subject │ help.AAL5Q2B3SM │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ AC7PO3MREV26 │ +│ Issued │ 2019-04-29 15:01:13 UTC │ +│ Expires │ │ +├─────────────────┼─────────────────────────┤ +│ Max Messages │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Network Src │ Any │ +│ Time │ Any │ +╰─────────────────┴─────────────────────────╯ +``` + +The token can be shared directly with the client account. + +> If you manage many tokens for many accounts, you may want to host activation tokens on a web server and share the URL with the account. The benefit to the hosted approach is that any updates to the token would be available to the importing account whenever their account is updated, provided the URL you host them in is stable. + +## Importing a Private Service + +As with streams, importing a private service is more natural than a public one because the activation token stores all the necessary details. Again, the token can be an actual file path or a remote URL. + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ AccountB │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-29 15:26:39 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Exports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭─────────────────────────────────────────────────────────────────────────────────╮ +│ Imports │ +├─────────┬─────────┬─────────────────┬─────────┬─────────┬──────────────┬────────┤ +│ Name │ Type │ Remote │ Local │ Expires │ From Account │ Public │ +├─────────┼─────────┼─────────────────┼─────────┼─────────┼──────────────┼────────┤ +│ help │ Service │ help.AAL5Q2B3SM │ help │ │ AC7PO3MREV26 │ No │ +╰─────────┴─────────┴─────────────────┴─────────┴─────────┴──────────────┴────────╯ +``` + + +### Testing the Private Service + +Start the replier: + +```text +> nats-rply -creds ~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds "help.>" "I will help" +Listening on [help.>] +``` + + +Send a request: +```text +> nats-req -creds ~/.nkeys/Test/accounts/AccountB/users/userb.creds help me +Published [help] : 'me' +``` + +The service receives the message: +```text +[#1] Received on [help.AAL5Q2B3SM]: 'me' +``` + +The requester receives its response: +```text +Received [_INBOX.N3IiqWbiAQfPoINCBpBrUM.ZjBNtkB3] : 'I will help' +``` \ No newline at end of file diff --git a/nats_tools/nsc/signing_keys.md b/nats_tools/nsc/signing_keys.md new file mode 100644 index 0000000..464c391 --- /dev/null +++ b/nats_tools/nsc/signing_keys.md @@ -0,0 +1,152 @@ +## Signing Keys + +As previously discussed, NKEYs are identities, and if someone gets a hold of an account or operator nkey they can do everything you can do as you. + +NATS has a strategies to let you deal with scenarios where your private keys escape out in the wild. + +The first and most important line of defense is _Signing Keys_. _Signing Keys_ allow you have multiple NKEY identities of the same kind (Operator or Account) that have the same degree of trust as the standard _Issuer_ nkey. + +The concept behind the signing key is that you can issue a JWT for an operator or an account that lists multiple nkeys. Typically the issuer will match the _Subject_ of the entity issuing the JWT. With SigningKeys, a JWT is considered valid if it is signed by the _Subject_ of the _Issuer_ or one of its signing keys. This enables guarding the private key of the Operator or Account more closely while allowing _Accounts_, _Users_ or _Activation Tokens_ be signed using alternate private keys. + +If an issue should arise where somehow a signing key escapes into the wild, you would remove the compromised signing key from the entity, add a new one, and reissue the entity. When a JWT is validated, if the signing key is missing, the operation is rejected. You are also on the hook to re-issue all JWTs (accounts, users, activation tokens) that were signed with the compromised signing key. + +This is effectively a large hammer. You can mitigate the process a bit by having a larger number of signing keys and then rotating the signing keys to get a distribution you can easily handle in case of a compromise. In a future release, we’ll have a revocation process were you can invalidate a single JWT by its unique JWT ID (JTI). For now a sledge hammer you have. + +With greater security process, there’s greater complexity. With that said, `nsc` doesn’t track public or private signing keys. As these are only identities that when in use presume a manual use. That means that you the user will have to track and manage your private keys more closely. + +Let’s get a feel for the workflow. We are going to: + +- Create an operator with a signing key +- Create an account with a signing key +- The account will be signed using the operator’s signing key +- Create an user with the account’s signing key + + +All signing key operations revolve around the global `nsc` flag `-K` or `--private-key`. Whenever you want to modify an entity, you have to supply the parent key so that the JWT is signed. Normally this happens automatically but in the case of signing keys, you’ll have to supply the flag by hand. + +Creating the operator: + +```text +> nsc add operator -n O2 +Generated operator key - private key stored "/Users/synadia/.nkeys/O2/O2.nk" +Success! - added operator "O2" + +``` + +To add a signing key we have to first generate one with `nk`. `NSC` doesn’t at this time offer a way to generate keys that are not associated with an entity. This means that you will have to generate and store the secrets yourself: + +```text +# generate an operator keypair: +> nk -gen operator -pubout +SOAIHSQSAM3ZJI5W6U5M4INH7FUCQQ5ETJ5RMPVJZCJLTDREY6ZNEE6LZQ +ODMYCI5TSZY6MFLOBBQ2RNRBRAXRKJKAC5UACRC6H6CJXCLR2STTGAAQ +``` + +> On a production environment private keys should be saved to a file and always referenced from the secured file. + +Now we are going to edit the operator by adding a signing key with the `--sk` flag providing the generated operator public key (the one starting with `O`): + +```text +> nsc edit operator --sk ODMYCI5TSZY6MFLOBBQ2RNRBRAXRKJKAC5UACRC6H6CJXCLR2STTGAAQ +Success! - edited operator +-----BEGIN NATS OPERATOR JWT----- +eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJPMk5BMkNaQ1ZINkQyTEVCQkNDVUFHTEZaWFJPTTdKTEs1Q1ZXRDZMVlpPVU9TUExDS0dBIiwiaWF0IjoxNTU2NTczNTYzLCJpc3MiOiJPQks3M09MUU9KV05ZVE4yTzQ2SVpRTjRXTVNDN0hWVk5BM1k2VFdQV0tDRlhJV1MzWExTQVVJUyIsIm5hbWUiOiJPMiIsInN1YiI6Ik9CSzczT0xRT0pXTllUTjJPNDZJWlFONFdNU0M3SFZWTkEzWTZUV1BXS0NGWElXUzNYTFNBVUlTIiwidHlwZSI6Im9wZXJhdG9yIiwibmF0cyI6eyJzaWduaW5nX2tleXMiOlsiT0RNWUNJNVRTWlk2TUZMT0JCUTJSTlJCUkFYUktKS0FDNVVBQ1JDNkg2Q0pYQ0xSMlNUVEdBQVEiXX19.-VNSZhmOa3TrGglTZ3pGU3BPScb0uj5rdvTHzzOyZ18_WlCBfo6H8S01S3D2qf9J36lKhPplMtupheYqEo04Aw +------END NATS OPERATOR JWT------ +``` + +Check our handy work: + +```text +> nsc describe operator +╭────────────────────────────────────────╮ +│ Operator Details │ +├──────────────┬─────────────────────────┤ +│ Name │ O2 │ +│ Operator ID │ OBK73OLQOJWN │ +│ Issuer ID │ OBK73OLQOJWN │ +│ Issued │ 2019-04-29 21:32:43 UTC │ +│ Expires │ │ +├──────────────┼─────────────────────────┤ +│ Signing Keys │ ODMYCI5TSZY6 │ +╰──────────────┴─────────────────────────╯ +``` + + +Now let’s create an account called `A` and sign it the generated operator private signing key. To sign it with the key specify the `-K` flag and the private key or a path to the private key: + +```text +> nsc add account --name A -K SOAIHSQSAM3ZJI5W6U5M4INH7FUCQQ5ETJ5RMPVJZCJLTDREY6ZNEE6LZQ +Generated account key - private key stored "/Users/synadia/.nkeys/O2/accounts/A/A.nk" +Success! - added account "A" +``` + +Let’s generate an account signing key, again we use `nk`: + +```text +> nk -gen account -pubout +SAAK3EL5BW4ZOR7JVTXZ4TJ6RQBSOIXK27AFPPSYVP4KDHJKSRQFVRAHIA +ABHYL27UAHHQXA5HLH2YWHFQBIP4YMPC7RNZ4PSFRAMJHSSZUUIXF2RV +``` + +Let’s add the signing key to the account, and remember to sign the account with the operator signing key: +```text +> nsc edit account --sk ABHYL27UAHHQXA5HLH2YWHFQBIP4YMPC7RNZ4PSFRAMJHSSZUUIXF2RV -K SOAIHSQSAM3ZJI5W6U5M4INH7FUCQQ5ETJ5RMPVJZCJLTDREY6ZNEE6LZQ +Success! - edited account "A" + + +> nsc describe account +╭─────────────────────────────────────────────────────╮ +│ Account Details │ +├───────────────────────────┬─────────────────────────┤ +│ Name │ A │ +│ Account ID │ AD7HDY5AS3LT │ +│ Issuer ID │ ODMYCI5TSZY6 │ +│ Issued │ 2019-04-30 22:33:13 UTC │ +│ Expires │ │ +├───────────────────────────┼─────────────────────────┤ +│ Signing Keys │ ABHYL27UAHHQ │ +├───────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Leaf Node Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├───────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +│ Exports │ None │ +╰───────────────────────────┴─────────────────────────╯ +``` + +We can see that the signing key `ABHYL27UAHHQ` was added to the account. Also the issuer is the operator signing key (specified by the `-K`). + +Now let’s create a user and signing it with account signing key starting with `ABHYL27UAHHQ`. + +```text +> nsc add user --name U -K SAAK3EL5BW4ZOR7JVTXZ4TJ6RQBSOIXK27AFPPSYVP4KDHJKSRQFVRAHIA +Generated user key - private key stored "/Users/synadia/.nkeys/O2/accounts/A/users/U.nk" +Generated user creds file "/Users/synadia/.nkeys/O2/accounts/A/users/U.creds" +Success! - added user "U" to "A" + +> nsc describe user +╭───────────────────────────────────────────╮ +│ User │ +├─────────────────┬─────────────────────────┤ +│ Name │ U │ +│ User ID │ UDYKZHLXFH56 │ +│ Issuer ID │ ABHYL27UAHHQ │ +│ Issuer Account │ AD7HDY5AS3LT │ +│ Issued │ 2019-04-30 22:43:46 UTC │ +│ Expires │ │ +├─────────────────┼─────────────────────────┤ +│ Max Messages │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Network Src │ Any │ +│ Time │ Any │ +╰─────────────────┴─────────────────────────╯ +``` + +As expected, the issuer is now the signing key we generated earlier. To map the user to the actual account, an `Issuer Account` field was added to the JWT that identifies the public key of account _A_. + diff --git a/nats_tools/nsc/streams.md b/nats_tools/nsc/streams.md new file mode 100644 index 0000000..4c3ba74 --- /dev/null +++ b/nats_tools/nsc/streams.md @@ -0,0 +1,294 @@ +## Streams + +To share messages you publish with other accounts, you have to _Export_ a _Stream_. _Exports_ are associated with the account performing the export and advertised in exporting account’s JWT. + +### Adding a Public Stream Export + +To add a stream to your account: + +```text +> nsc add export --name "abc" --subject "a.b.c.>" +Success! - added public stream export "abc" +``` + +> Note that we have exported stream with a subject that contains a wildcard. Any subject that matches the pattern will be exported. + +To check that the export is how you intended: + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ TestAccount │ +│ Account ID │ AC7PO3MREV26 │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-29 14:20:13 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭───────────────────────────────────╮ +│ Exports │ +├──────┬─────────┬─────────┬────────┤ +│ Name │ Type │ Subject │ Public │ +├──────┼─────────┼─────────┼────────┤ +│ abc │ Stream │ a.b.c.> │ Yes │ +╰──────┴─────────┴─────────┴────────╯ +``` + +Messages this account publishes on `a.b.c.>` will be forwarded to all accounts that import this stream. + +### Importing a Stream + +Importing a stream enables you to receive messages that are published by a different _Account_. To import a Stream, you have to create an _Import_. To create an _Import_ you need to know: + +- The exporting account’s public key +- The subject where the stream is published +- You can map the stream’s subject to a different subject +- Self-imports are not valid; you can only import streams from other accounts. + +To learn how to inspect a JWT from an account server, [check this article](../nas/inspecting_jwts.md). + +With the required information, we can add an import to the public stream. + +```text +> nsc add import --src-account AC7PO3MREV26U3LFZFP5BN3HAI32X3PKLBRVMPAETLEHWPQEUG7EJY4H --remote-subject "a.b.c.>" --local-subject "abc.>" +Success! - added stream import "a.b.c.>" +``` + +> Note we did fancy things here: The remote stream publishes messages as `a.b.c.>`, but we changed the prefix to be something else in the importing account’s subject space. We changed it to `abc.>`. Subscribers in our account can listen to `abc.>` to get the messages. + +And verifying it: + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ AccountB │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-25 21:33:58 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Exports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭────────────────────────────────────────────────────────────────────╮ +│ Imports │ +├───────┬────────┬─────────┬───────┬─────────┬──────────────┬────────┤ +│ Name │ Type │ Remote │ Local │ Expires │ From Account │ Public │ +├───────┼────────┼─────────┼───────┼─────────┼──────────────┼────────┤ +│ abc.> │ Stream │ a.b.c.> │ abc.> │ │ AC7PO3MREV26 │ Yes │ +╰───────┴────────┴─────────┴───────┴─────────┴──────────────┴────────╯ +``` + +## Securing Streams + +If you want to create a stream that is only accessible to accounts you designate you can create a _private_ stream. The export will be visible in your account, but _subscribing_ accounts will require an authorization token that must be created by you and generated specifically for the subscribing account. + +The authorization token is simply a JWT signed by your account where you authorize the client account to import your export. + +### Creating a Private Stream Export + +```text +> nsc add export --name pabc --subject "a.b.c.>" --private +Success! - added private stream export "pabc" +``` + +Like before we defined an export, but this time we added the `--private` flag. + +```text +> nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ TestAccount │ +│ Account ID │ AC7PO3MREV26 │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-25 21:51:02 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Imports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭──────────────────────────────────╮ +│ Exports │ +├──────┬────────┬─────────┬────────┤ +│ Name │ Type │ Subject │ Public │ +├──────┼────────┼─────────┼────────┤ +│ pabc │ Stream │ a.b.c.> │ No │ +╰──────┴────────┴─────────┴────────╯ +``` + + +### Generating an Activation Token + +For a foreign account to _import_ a private stream, you have to generate an activation token. The activation token in addition to granting permissions to the account, it also allows you to subset the exported stream’s subject. + +Let’s create an account and user for our stream client: +```text +> nsc add account --name AccountB +Generated account key - private key stored “~/.nkeys/Test/accounts/AccountB/AccountB" +Success! - added account "AccountB" + +> nsc add user --name userb +Generated user key - private key stored "~/.nkeys/Test/accounts/AccountB/users/userb” +Generated user creds file "~/.nkeys/Test/accounts/ACcountB/users/userb.creds" +Success! - added user “userb” to “AccountB” +``` + +To generate a token, you’ll need to know the public key of the account importing the stream. + +```text +> nsc generate activation -o /tmp/activation.jwt --target-account AAL5Q2B3SMSO5AS3APJFUNAIKUCEQJPAQ76XEBTVOCQCXXGKP3YMGGN4 —subject a.b.c.d +generated "pabc" activation for account "AAL5Q2B3SMSO5AS3APJFUNAIKUCEQJPAQ76XEBTVOCQCXXGKP3YMGGN4". +JTI is "VNT3Y32I5FNTEHIVL6PINEJNNZ6Z2BGGEJ2QWNA3TPQ4A4KBRGHQ" +``` + +In the above invocation, we generated an activation redirecting the output to `/tmp/activation.jwt`. The exporting account exported `a.b.c.>`, but on the activation token will only grant permission to `a.b.c.d` to the target account. + +For completeness, the contents of the JWT file look like this: + +```text +> cat /tmp/activation.jwt +-----BEGIN NATS ACTIVATION JWT----- +eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJWTlQzWTMySTVGTlRFSElWTDZQSU5FSk5OWjZaMkJHR0VKMlFXTkEzVFBRNEE0S0JSR0hRIiwiaWF0IjoxNTU2MjI5NDk0LCJpc3MiOiJBQzdQTzNNUkVWMjZVM0xGWkZQNUJOM0hBSTMyWDNQS0xCUlZNUEFFVExFSFdQUUVVRzdFSlk0SCIsIm5hbWUiOiJhLmIuYy5kIiwic3ViIjoiQUFMNVEyQjNTTVNPNUFTM0FQSkZVTkFJS1VDRVFKUEFRNzZYRUJUVk9DUUNYWEdLUDNZTUdHTjQiLCJ0eXBlIjoiYWN0aXZhdGlvbiIsIm5hdHMiOnsic3ViamVjdCI6ImEuYi5jLmQiLCJ0eXBlIjoic3RyZWFtIn19.eA0W-mcxFXyIpEk0MUgaLjj7t5jxEHTar7MNY5IYcJ7NHlDoHU5IFog2LlFW_hpTCFA4qa989vqECsiTuBuCAA +------END NATS ACTIVATION JWT------ +``` + +When decoded it looks like this: + +```text +> nsc describe jwt -f /tmp/activation.jwt +╭───────────────────────────────────────────╮ +│ Activation │ +├─────────────────┬─────────────────────────┤ +│ Import Type │ Stream │ +│ Import Subject │ a.b.c.d │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ AC7PO3MREV26 │ +│ Issued │ 2019-04-25 21:58:14 UTC │ +│ Expires │ │ +├─────────────────┼─────────────────────────┤ +│ Max Messages │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Network Src │ Any │ +│ Time │ Any │ +╰─────────────────┴─────────────────────────╯ +``` + +The token can be shared directly with the client account. + +> If you manage many tokens for many accounts, you may want to host activation tokens on a web server and share the URL with the account. The benefit to the hosted approach is that any updates to the token would be available to the importing account whenever their account is updated, provided the URL you host them in is stable. + +## Importing a Private Stream + +Importing a private stream is more natural than a public one as the activation token given to you already has all the necessary details. Note that the token can be an actual file path or a remote URL. + +```text +> nsc add import --token /tmp/activation.jwt +Success! - added stream import "a.b.c.d" +``` + +```text +> nsc describe account +nsc describe account +╭────────────────────────────────────────────────────╮ +│ Account Details │ +├──────────────────────────┬─────────────────────────┤ +│ Name │ AccountB │ +│ Account ID │ AAL5Q2B3SMSO │ +│ Issuer ID │ OAYI3YUZSWDN │ +│ Issued │ 2019-04-25 22:04:29 UTC │ +│ Expires │ │ +├──────────────────────────┼─────────────────────────┤ +│ Max Connections │ Unlimited │ +│ Max Data │ Unlimited │ +│ Max Exports │ Unlimited │ +│ Max Imports │ Unlimited │ +│ Max Msg Payload │ Unlimited │ +│ Max Subscriptions │ Unlimited │ +│ Exports Allows Wildcards │ True │ +├──────────────────────────┼─────────────────────────┤ +│ Exports │ None │ +╰──────────────────────────┴─────────────────────────╯ + +╭────────────────────────────────────────────────────────────────────────╮ +│ Imports │ +├─────────┬────────┬─────────┬─────────┬─────────┬──────────────┬────────┤ +│ Name │ Type │ Remote │ Local │ Expires │ From Account │ Public │ +├─────────┼────────┼─────────┼─────────┼─────────┼──────────────┼────────┤ +│ a.b.c.d │ Stream │ a.b.c.d │ a.b.c.d │ │ AC7PO3MREV26 │ No │ +╰─────────┴────────┴─────────┴─────────┴─────────┴──────────────┴────────╯ +``` + +### Testing the Private Stream + +Start the `nats-account-server`: +```text + > nats-account-server -nsc ~/.nsc/nats/Test +``` + +Create a config for the nats server `server.conf`: +```text +operator: /Users/synadia/.nsc/nats/Test/Test.jwt +resolver: URL(http://localhost:9090/jwt/v1/accounts/) +``` + +Start the `nats-server`: +```text +> nats-server -c server.conf +``` + +Start the subscriber for the client account: +```text +> nats-sub -creds ~/.nkeys/Test/accounts/AccountB/users/userb.creds ">" +Listening on [>] +``` + +Publish messages to the stream: + +```text +# Client won’t get this one since it only has permission +# for messages ‘a.b.c.d’ +> nats-pub -creds ~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds a.b.c.a "hello" +Published [a.b.c.a] : 'hello' + + > nats-pub -creds ~/.nkeys/Test/accounts/TestAccount/users/TestUser.creds a.b.c.d "hello" +Published [a.b.c.d] : 'hello' +``` + +The subscriber as expected prints a message on the stream that it was allowed to receive: + +```text +[#1] Received on [a.b.c.d.a.b.c.d]: 'hello' +``` + +