mirror of
https://github.com/taigrr/nats.docs
synced 2025-01-18 04:03:23 -08:00
Merge branch 'master' of https://github.com/nats-io/docs-site
This commit is contained in:
commit
349bb6284c
26
SUMMARY.md
26
SUMMARY.md
@ -15,9 +15,17 @@
|
|||||||
* [Tokens](nats_server/tokens.md)
|
* [Tokens](nats_server/tokens.md)
|
||||||
* [Username/Password](nats_server/username_password.md)
|
* [Username/Password](nats_server/username_password.md)
|
||||||
* [TLS Authentication](nats_server/tls_mutual_auth.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)
|
* [Authorization](nats_server/authorization.md)
|
||||||
* [Clustering](nats_server/clustering.md)
|
* [Clustering](nats_server/clustering.md)
|
||||||
|
* [Configuration](nats_server/cluster_config.md)
|
||||||
* [TLS Authentication](nats_server/cluster_tls.md)
|
* [TLS Authentication](nats_server/cluster_tls.md)
|
||||||
|
* [Gateways](gateways/README.md)
|
||||||
|
* [Configuration](gateways/gateway.md)
|
||||||
|
* [Leafnodes](leafnodes/README.md)
|
||||||
|
* [Configuration](leafnodes/leafnode_conf.md)
|
||||||
* [Logging](nats_server/logging.md)
|
* [Logging](nats_server/logging.md)
|
||||||
* [Monitoring](nats_server/monitoring.md)
|
* [Monitoring](nats_server/monitoring.md)
|
||||||
|
|
||||||
@ -25,16 +33,30 @@
|
|||||||
* [Upgrading a Cluster](nats_admin/upgrading_cluster.md)
|
* [Upgrading a Cluster](nats_admin/upgrading_cluster.md)
|
||||||
* [Slow Consumers](nats_admin/slow_consumers.md)
|
* [Slow Consumers](nats_admin/slow_consumers.md)
|
||||||
* [Signals](nats_admin/signals.md)
|
* [Signals](nats_admin/signals.md)
|
||||||
|
* [System Accounts](sys_accounts/README.md)
|
||||||
|
* [Configuration](sys_accounts/sys_accounts.md)
|
||||||
|
|
||||||
### NATS Tools
|
### NATS Tools
|
||||||
|
* [Tools](nats_tools/README.md)
|
||||||
* [mkpasswd](nats_tools/mkpasswd.md)
|
* [mkpasswd](nats_tools/mkpasswd.md)
|
||||||
* [NATS Top](nats_tools/nats_top/README.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-account-server](nats_tools/nas/README.md)
|
||||||
|
* [Basics](nats_tools/nas/nas_conf.md)
|
||||||
|
* [Inspecting JWTs](nats_tools/nas/inspecting_jwts.md)
|
||||||
|
* [Memory Resolver](nats_tools/nas/mem_resolver.md)
|
||||||
|
* [nats-top](nats_tools/nats_top/README.md)
|
||||||
* [Tutorial](nats_tools/nats_top/tutorial.md)
|
* [Tutorial](nats_tools/nats_top/tutorial.md)
|
||||||
* [Benchmarking](nats_tools/natsbench.md)
|
* [nats-bench](nats_tools/natsbench.md)
|
||||||
|
|
||||||
### NATS Containerization
|
### NATS Containerization
|
||||||
* [NATS.docker](nats_docker/README.md)
|
* [NATS.docker](nats_docker/README.md)
|
||||||
* [Tutorial](nats_docker/tutorial.md)
|
* [Tutorial](nats_docker/tutorial.md)
|
||||||
|
* [Docker Swarm](nats_docker/docker_swarm.md)
|
||||||
|
|
||||||
## NATS Streaming Server
|
## NATS Streaming Server
|
||||||
* [Basics](nats_streaming/nats-streaming-intro.md)
|
* [Basics](nats_streaming/nats-streaming-intro.md)
|
||||||
|
76
gateways/README.md
Normal file
76
gateways/README.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
## Gateways
|
||||||
|
|
||||||
|
Gateways enable connecting one or more clusters together; they allow the formation of super clusters from smaller clusters. Cluster and Gateway protocols listen in different ports. Clustering is used for adjacent servers; gateways are for joining clusters together. Typically all cluster nodes will also be gateway nodes, but this is not a requirement.
|
||||||
|
|
||||||
|
Gateway configuration is similar to clustering:
|
||||||
|
|
||||||
|
- gateways have a dedicated port where they listen for gateway requests
|
||||||
|
- gateways gossip gateway members and remote discovered gateways
|
||||||
|
|
||||||
|
Unlike clusters, gateways:
|
||||||
|
|
||||||
|
- don't form a full mesh
|
||||||
|
- are bound by uni-directional connections
|
||||||
|
|
||||||
|
Gateways exist to:
|
||||||
|
|
||||||
|
- reduce the number of connections required between servers
|
||||||
|
- optimize the interest graph propagation
|
||||||
|
|
||||||
|
## Gateway Connections
|
||||||
|
|
||||||
|
A nats-server in a gateway role will specify a port where it will accept gateways connections. If the configuration specifies other _external_ `gateways`, the gateway will create one outbound gateway connection for each gateway in its configuration. It will also gossip other gateways it knows or discovered.
|
||||||
|
|
||||||
|
If the local cluster has three gateway nodes, this means there will be three outbound connections to each external gateway.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> In the example above cluster _A_ has configured gateway connections for _B_ (solid lines). B has discovered gateway connections to _A_ (dotted lines). Note that the number of outgoing connections always matches the number of gateways with the same name.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> In this second example, again configured connections are shown with solid lines and discovered gateway connections are shown using dotted lines. Gateways _A_ and _C_ were both discovered via gossiping; _B_ discovered _A_ and _A_ discovered _C_.
|
||||||
|
|
||||||
|
|
||||||
|
A key point in the description above is that each node in the cluster will make a connection to a single node in the remote cluster — a difference from the clustering protocol, where every node is directly connected to all other nodes.
|
||||||
|
|
||||||
|
For those mathematically inclined, cluster connections are `N(N-1)/2` where _N_ is the number of nodes in the cluster. On gateway configurations, outbound connections are the summation of `Ni(M-1)` where Ni is the number of nodes in a gateway _i_, and _M_ is the total number of gateways. Inbound connections are the summation of `U-Ni` where U is the sum of all gateway nodes in all gateways, and N is the number of nodes in a gateway _i_. It works out that both inbound and outbound connection counts are the same.
|
||||||
|
|
||||||
|
The number of connections required to join clusters using clustering vs. gateways is apparent very quickly. For 3 clusters, with N nodes:
|
||||||
|
|
||||||
|
| Nodes per Cluster | Full Mesh Conns | Gateway Conns |
|
||||||
|
| ---: | ----: | ----: |
|
||||||
|
| 1 | 3 | 6|
|
||||||
|
| 2 | 15 | 12 |
|
||||||
|
| 3 | 36 | 18 |
|
||||||
|
| 4 | 66 | 24 |
|
||||||
|
| 5 | 105 | 30 |
|
||||||
|
| 30 | 4005 | 180 |
|
||||||
|
|
||||||
|
## Interest Propagation
|
||||||
|
|
||||||
|
Gateways propagate interest using three different mechanisms:
|
||||||
|
|
||||||
|
- Optimistic Mode
|
||||||
|
- Interest-only Mode
|
||||||
|
- Queue Subscriptions
|
||||||
|
|
||||||
|
### Optimistic Mode
|
||||||
|
|
||||||
|
When a publisher in _A_ publishes "foo", the _A_ gateway will check if cluster _B_ has registered _no_ interest in "foo". If not, it forwards "foo" to _B_. If upon receiving "foo", _B_ has no subscribers on "foo", _B_ will send a gateway protocol message to _A_ expressing that it has no interest on "foo", preventing future messages on "foo" from being forwarded.
|
||||||
|
|
||||||
|
Should a subscriber on _B_ create a subscription to "foo", _B_ knowing that it had previously rejected interest on _foo_, will send a gateway protocol message to cancel its previous _no interest_ on "foo" in _A_.
|
||||||
|
|
||||||
|
### Interest-only Mode
|
||||||
|
|
||||||
|
When a gateway on _A_ sends many messages on various subjects for which _B_ has no interest. _B_ sends a gateway protocol message for _A_ to stop sending optimistically, and instead send if there's known interest in the subject. As subscriptions come and go on _B_, _B_ will update its subject interest with _A_.
|
||||||
|
|
||||||
|
### Queue Subscriptions
|
||||||
|
|
||||||
|
When a queue subscriber creates a new subscription, the gateway propagates the subscription interest to other gateways. The subscription interest is only propagated _once_ per _Account_ and subject. When the last queue subscriber is gone, the cluster interest is removed.
|
||||||
|
|
||||||
|
Queue subscriptions work on _Interest-only Mode_ to honor NATS' queue semantics across the _Super Cluster_. For each queue group, a message is only delivered to a single queue subscriber. Only when a local queue group member is not found, is a message forwarded to a different interested cluster; gateways will always try to serve local queue subscribers first and only failover when a local queue subscriber is not found.
|
||||||
|
|
||||||
|
### Gateway Configuration
|
||||||
|
|
||||||
|
The [Gateway Configuration](gateway.md) document describes all the options available to gateways.
|
85
gateways/gateway.md
Normal file
85
gateways/gateway.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
## Gateway Configuration
|
||||||
|
|
||||||
|
The `gateway` configuration block is similar to a `cluster` block:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
gateway {
|
||||||
|
name: "A"
|
||||||
|
listen: "localhost:7222"
|
||||||
|
authorization {
|
||||||
|
user: gwu
|
||||||
|
password: gwp
|
||||||
|
}
|
||||||
|
|
||||||
|
gateways: [
|
||||||
|
{name: "A", url: "nats-gateway://gwu:gwp@localhost:7222"},
|
||||||
|
{name: "B", url: "nats-gateway://gwu:gwp@localhost:7333"},
|
||||||
|
{name: "C", url: "nats-gateway://gwu:gwp@localhost:7444"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
One difference is that instead of `routes` you specify `gateways`. As expected _self-gateway_ connections are ignored, so you can share gateway configurations with minimal fuzz.
|
||||||
|
|
||||||
|
Starting a server:
|
||||||
|
```text
|
||||||
|
> nats-server -c A.conf
|
||||||
|
[85803] 2019/05/07 10:50:55.902474 [INF] Starting nats-server version 2.0.0-RC11
|
||||||
|
[85803] 2019/05/07 10:50:55.902547 [INF] Git commit [not set]
|
||||||
|
[85803] 2019/05/07 10:50:55.903669 [INF] Gateway name is A
|
||||||
|
[85803] 2019/05/07 10:50:55.903684 [INF] Listening for gateways connections on localhost:7222
|
||||||
|
[85803] 2019/05/07 10:50:55.903696 [INF] Address for gateway "A" is localhost:7222
|
||||||
|
[85803] 2019/05/07 10:50:55.903909 [INF] Listening for client connections on 0.0.0.0:4222
|
||||||
|
[85803] 2019/05/07 10:50:55.903914 [INF] Server id is NBHUDBF3TVJSWCDPG2HSKI4I2SBSPDTNYEXEMOFAZUZYXVA2IYRUGPZU
|
||||||
|
[85803] 2019/05/07 10:50:55.903917 [INF] Server is ready
|
||||||
|
[85803] 2019/05/07 10:50:56.830669 [INF] 127.0.0.1:50892 - gid:2 - Processing inbound gateway connection
|
||||||
|
[85803] 2019/05/07 10:50:56.830673 [INF] 127.0.0.1:50891 - gid:1 - Processing inbound gateway connection
|
||||||
|
[85803] 2019/05/07 10:50:56.831079 [INF] 127.0.0.1:50892 - gid:2 - Inbound gateway connection from "C" (NBHWDFO3KHANNI6UCEUL27VNWL7NWD2MC4BI4L2C7VVLFBSMZ3CRD7HE) registered
|
||||||
|
[85803] 2019/05/07 10:50:56.831211 [INF] 127.0.0.1:50891 - gid:1 - Inbound gateway connection from "B" (ND2UJB3GFUHXOQ2UUMZQGOCL4QVR2LRJODPZH7MIPGLWCQRARJBU27C3) registered
|
||||||
|
[85803] 2019/05/07 10:50:56.906103 [INF] Connecting to explicit gateway "B" (localhost:7333) at 127.0.0.1:7333
|
||||||
|
[85803] 2019/05/07 10:50:56.906104 [INF] Connecting to explicit gateway "C" (localhost:7444) at 127.0.0.1:7444
|
||||||
|
[85803] 2019/05/07 10:50:56.906404 [INF] 127.0.0.1:7333 - gid:3 - Creating outbound gateway connection to "B"
|
||||||
|
[85803] 2019/05/07 10:50:56.906444 [INF] 127.0.0.1:7444 - gid:4 - Creating outbound gateway connection to "C"
|
||||||
|
[85803] 2019/05/07 10:50:56.906647 [INF] 127.0.0.1:7444 - gid:4 - Outbound gateway connection to "C" (NBHWDFO3KHANNI6UCEUL27VNWL7NWD2MC4BI4L2C7VVLFBSMZ3CRD7HE) registered
|
||||||
|
[85803] 2019/05/07 10:50:56.906772 [INF] 127.0.0.1:7333 - gid:3 - Outbound gateway connection to "B" (ND2UJB3GFUHXOQ2UUMZQGOCL4QVR2LRJODPZH7MIPGLWCQRARJBU27C3) registered
|
||||||
|
```
|
||||||
|
|
||||||
|
Once all the gateways are up, these clusters of one will forward messages as expected:
|
||||||
|
```text
|
||||||
|
> nats-pub -s localhost:4444 foo bar
|
||||||
|
Published [foo] : 'bar'
|
||||||
|
|
||||||
|
# On a different session...
|
||||||
|
> nats-sub -s localhost:4333 ">"
|
||||||
|
Listening on [>]
|
||||||
|
[#1] Received on [foo]: 'bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `Gateway` Configuration Block
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `advertise` | Hostport `<host>:<port>` to advertise to other gateways. |
|
||||||
|
| `authorization` | Authorization block (same as other nats-server `authorization` configuration). |
|
||||||
|
| `connect_retries` | Number of times the server will try to connect to a discovered gateway. |
|
||||||
|
| `gateways` | List of Gateway entries - see below. |
|
||||||
|
| `host` | Interface where the gateway will listen for incomming gateway connections. |
|
||||||
|
| `listen` | Combines `host` and `port` as `<host>:<port>` |
|
||||||
|
| `name` | Name for this cluster, all gateways belonging to the same cluster, should specify the same name. |
|
||||||
|
| `port` | Port where the gateway will listen for incomming gateway connections. |
|
||||||
|
| `reject_unknown` | If `true`, gateway will reject connections from gateways that are not configured in `gateways`. |
|
||||||
|
| `tls` | TLS configuration block (same as other [nats-server `tls` configuration](/nats_server/tls.md#tls-configuration)). |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### `Gateway` Entry
|
||||||
|
|
||||||
|
The `gateways` configuration block is a list of gateway entries with the following properties:
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `name` | Gateway name. |
|
||||||
|
| `url` | Hostport `<host>:<port>` describing where the remote gateway can be reached. If multiple IPs are returned, one is randomly selected. |
|
||||||
|
| `urls` | A list of `url` |
|
||||||
|
|
76
gateways/simple.svg
Normal file
76
gateways/simple.svg
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="152 192 358 280" width="358" height="280">
|
||||||
|
<defs>
|
||||||
|
<font-face font-family="Helvetica Neue" font-size="16" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
|
||||||
|
<font-face-src>
|
||||||
|
<font-face-name name="HelveticaNeue"/>
|
||||||
|
</font-face-src>
|
||||||
|
</font-face>
|
||||||
|
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black">
|
||||||
|
<g>
|
||||||
|
<path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<metadata> Produced by OmniGraffle 7.10.2
|
||||||
|
<dc:date>2019-05-07 16:42:18 +0000</dc:date>
|
||||||
|
</metadata>
|
||||||
|
<g id="Canvas_1" fill-opacity="1" stroke="none" stroke-dasharray="none" fill="none" stroke-opacity="1">
|
||||||
|
<title>Canvas 1</title>
|
||||||
|
<rect fill="white" x="152" y="192" width="358" height="280"/>
|
||||||
|
<g id="Canvas_1: Layer 1">
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g id="Graphic_5">
|
||||||
|
<circle cx="185.75" cy="225.75" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="225.75" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 215.75)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A1</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_6">
|
||||||
|
<circle cx="185.75" cy="332.25" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="332.25" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 322.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A2</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_7">
|
||||||
|
<circle cx="185.75" cy="438.25" r="33.2500531302381" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="438.25" r="33.2500531302381" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 428.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A3</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_9">
|
||||||
|
<circle cx="476.25" cy="225.75" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="476.25" cy="225.75" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(454.65 215.75)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.672" y="15">B1</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_8">
|
||||||
|
<circle cx="476.25" cy="332.25" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="476.25" cy="332.25" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(454.65 322.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.672" y="15">B2</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Line_10">
|
||||||
|
<line x1="219.00002" y1="225.75" x2="433.1" y2="225.75" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_11">
|
||||||
|
<line x1="216.9757" y1="320.80236" x2="435.72924" y2="240.6053" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_12">
|
||||||
|
<line x1="216.99302" y1="426.8498" x2="435.70677" y2="347.04374" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_14">
|
||||||
|
<line x1="449.4098" y1="245.38355" x2="220.58062" y2="412.7715" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,5.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_15">
|
||||||
|
<line x1="443" y1="332.25" x2="228.90002" y2="332.25" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,5.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
109
gateways/three_gw.svg
Normal file
109
gateways/three_gw.svg
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="152 140.7461 648.5 356.93095" width="648.5" height="356.93095">
|
||||||
|
<defs>
|
||||||
|
<font-face font-family="Helvetica Neue" font-size="16" panose-1="2 0 5 3 0 0 0 2 0 4" units-per-em="1000" underline-position="-100" underline-thickness="50" slope="0" x-height="517" cap-height="714" ascent="951.9958" descent="-212.99744" font-weight="400">
|
||||||
|
<font-face-src>
|
||||||
|
<font-face-name name="HelveticaNeue"/>
|
||||||
|
</font-face-src>
|
||||||
|
</font-face>
|
||||||
|
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black">
|
||||||
|
<g>
|
||||||
|
<path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
</marker>
|
||||||
|
<marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" stroke-linejoin="miter" stroke-miterlimit="10" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black">
|
||||||
|
<g>
|
||||||
|
<path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
</marker>
|
||||||
|
</defs>
|
||||||
|
<metadata> Produced by OmniGraffle 7.10.2
|
||||||
|
<dc:date>2019-05-07 16:43:34 +0000</dc:date>
|
||||||
|
</metadata>
|
||||||
|
<g id="Canvas_1" fill-opacity="1" stroke="none" stroke-dasharray="none" fill="none" stroke-opacity="1">
|
||||||
|
<title>Canvas 1</title>
|
||||||
|
<rect fill="white" x="152" y="140.7461" width="648.5" height="356.93095"/>
|
||||||
|
<g id="Canvas_1: Layer 1">
|
||||||
|
<title>Layer 1</title>
|
||||||
|
<g id="Graphic_5">
|
||||||
|
<circle cx="185.75" cy="225.75" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="225.75" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 215.75)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A1</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_6">
|
||||||
|
<circle cx="185.75" cy="332.25" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="332.25" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 322.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A2</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_7">
|
||||||
|
<circle cx="185.75" cy="438.25" r="33.2500531302381" fill="white"/>
|
||||||
|
<circle cx="185.75" cy="438.25" r="33.2500531302381" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(164.15 428.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.968" y="15">A3</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_9">
|
||||||
|
<circle cx="476.25" cy="225.75" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="476.25" cy="225.75" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(454.65 215.75)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.672" y="15">B1</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_8">
|
||||||
|
<circle cx="476.25" cy="332.25" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="476.25" cy="332.25" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(454.65 322.25)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.672" y="15">B2</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Line_10">
|
||||||
|
<line x1="219.00002" y1="225.75" x2="433.1" y2="225.75" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_11">
|
||||||
|
<line x1="216.9757" y1="320.80236" x2="435.72924" y2="240.6053" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_12">
|
||||||
|
<line x1="216.99302" y1="426.8498" x2="435.70677" y2="347.04374" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_14">
|
||||||
|
<line x1="449.4098" y1="245.38355" x2="220.58062" y2="412.7715" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,5.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_15">
|
||||||
|
<line x1="443" y1="332.25" x2="228.90002" y2="332.25" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,5.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Graphic_18">
|
||||||
|
<circle cx="766.75" cy="225.75" r="33.250053130238" fill="white"/>
|
||||||
|
<circle cx="766.75" cy="225.75" r="33.250053130238" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
<text transform="translate(745.15 215.75)" fill="black">
|
||||||
|
<tspan font-family="Helvetica Neue" font-size="16" font-weight="400" fill="black" x="11.376" y="15">C1</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Line_19">
|
||||||
|
<line x1="509.5" y1="225.75" x2="723.6" y2="225.75" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_24">
|
||||||
|
<path d="M 737.9152 209.17695 C 691.19614 184.57088 592.20205 141.2461 474.34375 141.2461 C 363.9938 141.2461 271.8946 179.22656 223.17214 204.26238" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,4.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_25">
|
||||||
|
<path d="M 215.77826 452.5492 C 273.97692 477.73306 407.3362 523.2367 531.3711 477.8711 C 649.03855 434.8344 718.7598 324.48573 748.9529 265.0568" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,4.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_26">
|
||||||
|
<path d="M 206.45157 358.2732 C 244.25963 400.88577 331.892 478.7072 453.0625 456.4961 C 575.3629 434.07786 689.0991 317.5855 739.4792 259.19245" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,4.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_27">
|
||||||
|
<path d="M 199.5272 256.02128 C 228.04436 312.15083 301.47194 426.71484 425.75 426.71484 C 549.392 426.71484 679.6919 313.32052 736.8824 256.88767" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,4.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_28">
|
||||||
|
<line x1="507.4757" y1="320.80236" x2="726.2292" y2="240.6053" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
<g id="Line_30">
|
||||||
|
<path d="M 736.5963 211.71723 C 709.7235 200.97972 667.2586 188 619 188 C 577.0519 188 540.7803 197.8071 515.29633 207.424" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.0,4.0" stroke-width="1"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
208
leafnodes/README.md
Normal file
208
leafnodes/README.md
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
## Leaf Nodes
|
||||||
|
|
||||||
|
A _Leaf Node_ is a separate authentication domain. Leaf Nodes leverage [Accounts](../nats_server/jwt_auth.md) and JWT to enable a server to connect to another and filter messages as per the leaf node's account User configuration.
|
||||||
|
|
||||||
|
This effectively means that the leaf node cluster's with the other server at an Account level:
|
||||||
|
|
||||||
|
- Leaf nodes clients authenticate locally (or just connect if authentication is not required)
|
||||||
|
- Traffic between the leaf node and the cluster assume the restrictions of the User configuration used to create the leaf connection.
|
||||||
|
- Subjects that the user is allowed to publish are exported to the cluster.
|
||||||
|
- Subjects the user is allowed to subscribe to, are imported into the leaf node.
|
||||||
|
|
||||||
|
> Leaf Nodes are an important component as a way to bridge traffic between local nats-servers you control and servers that are managed by a third-party. The Synadia's [NATS Global Service (NGS)](https://www.synadia.com/) allows accounts to use leaf nodes, but gain accessibility to the global network to inexpensively connect geographically distributed servers or small clusters.
|
||||||
|
|
||||||
|
[LeafNode Configuration Options](leafnode_conf.md)
|
||||||
|
|
||||||
|
### LeafNode Configuration Tutorial
|
||||||
|
|
||||||
|
Create a new operator called "O":
|
||||||
|
```text
|
||||||
|
> nsc add operator -n O
|
||||||
|
Generated operator key - private key stored "~/.nkeys/O/O.nk"
|
||||||
|
Success! - added operator "O"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an account called "A":
|
||||||
|
```text
|
||||||
|
> nsc add account -n A
|
||||||
|
Generated account key - private key stored "~/.nkeys/O/accounts/A/A.nk"
|
||||||
|
Success! - added account "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an user called "leaf":
|
||||||
|
```text
|
||||||
|
> nsc add user -n leaf
|
||||||
|
Generated user key - private key stored "~/.nkeys/O/accounts/A/users/leaf.nk"
|
||||||
|
Generated user creds file "~/.nkeys/O/accounts/A/users/leaf.creds"
|
||||||
|
Success! - added user "leaf" to "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's create an second user called 'nolimit'
|
||||||
|
```text
|
||||||
|
> nsc add user -n nolimit
|
||||||
|
Generated user key - private key stored "~/.nkeys/O/accounts/A/users/nolimit.nk"
|
||||||
|
Generated user creds file "~/.nkeys/O/accounts/A/users/nolimit.creds"
|
||||||
|
Success! - added user "nolimit" to "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
Start a nats-account-server:
|
||||||
|
```text
|
||||||
|
> nats-account-server -nsc ~/.nsc/nats/O
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the server configuration file (server.conf) with the following contents:
|
||||||
|
```text
|
||||||
|
operator: /Users/synadia/.nsc/nats/O/O.jwt
|
||||||
|
resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
leafnodes {
|
||||||
|
listen: "127.0.0.1:4000"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The server configuration naturally requires an `operator` and `resolver` to deal with the JWT authentication and accounts. In addition the `leafnodes` configuration exposes a `listen` where the server will receive leaf nodes. In this case on the localhost on port 4000.
|
||||||
|
|
||||||
|
Start the nats-server:
|
||||||
|
```text
|
||||||
|
> nats-server -c server.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a subscriber on the server:
|
||||||
|
```text
|
||||||
|
> nats-sub -creds ~/.nkeys/O/accounts/A/users/nolimit.creds ">"
|
||||||
|
Listening on [>]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Create the leaf server configuration (leaf.conf) with the following contents:
|
||||||
|
```text
|
||||||
|
port: 4111
|
||||||
|
leafnodes {
|
||||||
|
remotes = [
|
||||||
|
{ url: nats-leaf://localhost:4000,
|
||||||
|
credentials: /Users/synadia/.nkeys/O/accounts/A/users/leaf.creds
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note the leaf node configuration lists a number of `remotes`. The `url` specifies the port on the server where leaf node connections are allowed. The `credentials` configuration specifies the path to a user's credentials file.
|
||||||
|
|
||||||
|
Create a subscriber on the leaf:
|
||||||
|
```text
|
||||||
|
> nats-sub -s localhost:4111 ">"
|
||||||
|
Listening on [>]
|
||||||
|
```
|
||||||
|
|
||||||
|
Publish a message on the server:
|
||||||
|
```text
|
||||||
|
> nats-pub -creds ~/.nkeys/O/accounts/A/users/leaf.creds foo foo
|
||||||
|
Published [foo] : 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the server and leaf subscriber print:
|
||||||
|
```text
|
||||||
|
[#1] Received on [foo]: 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
Publish a message on the leaf:
|
||||||
|
```text
|
||||||
|
> nats-pub -s localhost:4111 bar bar
|
||||||
|
Published [bar] : 'bar'
|
||||||
|
```
|
||||||
|
Both the server and leaf subscribers print:
|
||||||
|
```text
|
||||||
|
[#2] Received on [bar]: 'bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
The leaf forwards all local messages to the server where members of the account are able to receive them. Messages published on the server by the account are forwarded to the leaf where subscribers are able to receive them.
|
||||||
|
|
||||||
|
### Leaf Authorization
|
||||||
|
|
||||||
|
In some cases you may want to restrict what messages can be exported from the leaf node or imported from the account. For leaf servers this is simply a user account configuration, as users can have specific permissions on what subjects to publish and/or subscribe to.
|
||||||
|
|
||||||
|
Let's put some restrictions on the `leaf` user so that it can only publish to `foo` and subscribe to `bar`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nsc edit user -n leaf --allow-pub foo --allow-sub bar
|
||||||
|
Updated user creds file "~/.nkeys/O/accounts/A/users/leaf.creds"
|
||||||
|
Success! - edited user "leaf" in account "A"
|
||||||
|
|
||||||
|
-----BEGIN NATS ACCOUNT JWT-----
|
||||||
|
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJVSk9RTFVSTUVFTVZXQVpVT0E2VlE1UVQ0UEdIV081WktDWlBLVFBJQVpLSldaSTJGNVpRIiwiaWF0IjoxNTU2ODM1MzU4LCJpc3MiOiJBRDU3TUZOQklLTzNBRFU2VktMRkVYQlBVQjdFWlpLU0tVUDdZTzNWVUFJTUlBWUpVNE1EM0NDUiIsIm5hbWUiOiJsZWFmIiwic3ViIjoiVUNEMlpSVUs1UE8yMk02MlNWVTZITzZJS01BVERDUlJYVVVGWDRRU1VTWFdRSDRHU1Y3RDdXVzMiLCJ0eXBlIjoidXNlciIsIm5hdHMiOnsicHViIjp7ImFsbG93IjpbImZvbyJdfSwic3ViIjp7ImFsbG93IjpbImJhciJdfX19.IeqSylTaisMQMH3Ih_0G8LLxoxe0gIClpxTm3B_ys_XwL9TtPIW-M2qdaYQZ_ZmR2glMvYK4EJ6J8RQ1UZdGAg
|
||||||
|
------END NATS ACCOUNT JWT------
|
||||||
|
|
||||||
|
> nsc describe user -n leaf
|
||||||
|
╭───────────────────────────────────────────╮
|
||||||
|
│ User │
|
||||||
|
├─────────────────┬─────────────────────────┤
|
||||||
|
│ Name │ leaf │
|
||||||
|
│ User ID │ UCD2ZRUK5PO2 │
|
||||||
|
│ Issuer ID │ AD57MFNBIKO3 │
|
||||||
|
│ Issued │ 2019-05-02 22:15:58 UTC │
|
||||||
|
│ Expires │ │
|
||||||
|
├─────────────────┼─────────────────────────┤
|
||||||
|
│ Pub Allow │ foo │
|
||||||
|
│ Sub Allow │ bar │
|
||||||
|
├─────────────────┼─────────────────────────┤
|
||||||
|
│ Max Messages │ Unlimited │
|
||||||
|
│ Max Msg Payload │ Unlimited │
|
||||||
|
│ Network Src │ Any │
|
||||||
|
│ Time │ Any │
|
||||||
|
╰─────────────────┴─────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
As we can see on the inspection of the user, the restrictions have been applied.
|
||||||
|
|
||||||
|
Let's repeat the experiment. This time we'll restart the leaf server so that the new user configuration is applied:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nats-server -c leaf.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see a new message on the leaf subscriber:
|
||||||
|
```text
|
||||||
|
Reconnected [nats://localhost:4111]
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's publish a message on the leaf:
|
||||||
|
```text
|
||||||
|
> nats-pub -s localhost:4111 foo foo
|
||||||
|
Published [foo] : 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see a new message in all your subscriber windows:
|
||||||
|
```text
|
||||||
|
[#3] Received on [foo]: 'foo'
|
||||||
|
```
|
||||||
|
|
||||||
|
Now publish a new message on the leaf, but this time with the subject `bar`:
|
||||||
|
```text
|
||||||
|
> nats-pub -s localhost:4111 bar bar
|
||||||
|
Published [bar] : 'bar'
|
||||||
|
```
|
||||||
|
|
||||||
|
This time only the leaf subscriber will print `[#4] Received on [bar]: 'bar'`, the account subscriber won't print it because the leaf user doesn't have permissions to publish on 'bar'.
|
||||||
|
|
||||||
|
|
||||||
|
Let's try the flow of messages from the server to the leaf node:
|
||||||
|
```
|
||||||
|
> nats-pub -creds ~/.nkeys/O/accounts/A/users/leaf.creds foo foo
|
||||||
|
Published [foo] : 'foo'
|
||||||
|
```
|
||||||
|
Only the server subscriber will receive the message as expected.
|
||||||
|
|
||||||
|
Repeat the publish this time with 'bar':
|
||||||
|
|
||||||
|
```
|
||||||
|
> nats-pub -creds ~/.nkeys/O/accounts/A/users/leaf.creds bar bar
|
||||||
|
Published [bar] : 'bar'
|
||||||
|
```
|
||||||
|
Both subscribers will receive the message as expected.
|
||||||
|
|
||||||
|
As you can see:
|
||||||
|
|
||||||
|
- Messages to and from the leaf node to the server are limitted by the user associated with the leaf node connection.
|
||||||
|
- Messages within the leaf node are as per the server's authentication and authorization configuration
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
37
leafnodes/leafnode_conf.md
Normal file
37
leafnodes/leafnode_conf.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
## `LeafNode` Configuration Block
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `advertise` | Hostport `<host>:<port>` to advertise to other gateways. |
|
||||||
|
| `authorization` | Authorization block (same as other nats-server `authorization` configuration). |
|
||||||
|
| `host` | Interface where the gateway will listen for incomming gateway connections. |
|
||||||
|
| `listen` | Combines `host` and `port` as `<host>:<port>` |
|
||||||
|
| `name` | Name for this cluster, all gateways belonging to the same cluster, should specify the same name. |
|
||||||
|
| `no_advertise | if `true` the leafnode shouldn't be advertised. |
|
||||||
|
| `port` | Port where the gateway will listen for incomming gateway connections. |
|
||||||
|
| `remotes` | List of `remote` entries specifying servers where leafnode client connection can be made. |
|
||||||
|
| `tls` | TLS configuration block (same as other nats-server `tls` configuration). |
|
||||||
|
|
||||||
|
|
||||||
|
### LeafNode `Remote` Entry Block
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `url` | Leafnode URL (URL protocol should be `leafnode`). |
|
||||||
|
| `account` | Account public key identifying the leafnode. Account must be defined locally. |
|
||||||
|
| `credentials` | Credential file for connecting to the leafnode server. |
|
||||||
|
| `tls` | A TLS configuration block. Gateway client will use specified TLS certificates when connecting/authenticating. |
|
||||||
|
|
||||||
|
### `TLS` Configuration Block
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `cert_file` | TLS certificate file. |
|
||||||
|
| `key_file` | TLS certificate key file. |
|
||||||
|
| `ca_file` | TLS certificate authority file. |
|
||||||
|
| `insecure` | Skip certificate verfication. |
|
||||||
|
| `verify` | If `true`, require and verify client certificates. |
|
||||||
|
| `verify_and_map` | If `true`, require and verify client certificates and use values map certificate values for authentication purposes. |
|
||||||
|
| `cipher_suites` | When set, only the specified TLS cipher suites will be allowed. Values must match golang version used to build the server. |
|
||||||
|
| `curve_preferences` | List of TLS cypher curves to use in order. |
|
||||||
|
| `timeout` | TLS handshake timeout in fractional seconds. |
|
94
nats_docker/docker_swarm.md
Normal file
94
nats_docker/docker_swarm.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
#### Step 1:
|
||||||
|
Create an overlay network for the cluster (in this example, `nats-cluster-example`), and instantiate an initial NATS server.
|
||||||
|
|
||||||
|
First create an overlay network:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker network create --driver overlay nats-cluster-example
|
||||||
|
```
|
||||||
|
|
||||||
|
Next instantiate an initial "seed" server for a NATS cluster listening for other servers to join route to it on port 6222:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service create --network nats-cluster-example --name nats-cluster-node-1 nats:1.0.0 -cluster nats://0.0.0.0:6222 -DV
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 2:
|
||||||
|
The 2nd step is to create another service which connects to the NATS server within the overlay network. Note that we connect to to the server at `nats-cluster-node-1`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service create --name ruby-nats --network nats-cluster-example wallyqs/ruby-nats:ruby-2.3.1-nats-v0.8.0 -e '
|
||||||
|
NATS.on_error do |e|
|
||||||
|
puts "ERROR: #{e}"
|
||||||
|
end
|
||||||
|
NATS.start(:servers => ["nats://nats-cluster-node-1:4222"]) do |nc|
|
||||||
|
inbox = NATS.create_inbox
|
||||||
|
puts "[#{Time.now}] Connected to NATS at #{nc.connected_server}, inbox: #{inbox}"
|
||||||
|
|
||||||
|
nc.subscribe(inbox) do |msg, reply|
|
||||||
|
puts "[#{Time.now}] Received reply - #{msg}"
|
||||||
|
end
|
||||||
|
|
||||||
|
nc.subscribe("hello") do |msg, reply|
|
||||||
|
next if reply == inbox
|
||||||
|
puts "[#{Time.now}] Received greeting - #{msg} - #{reply}"
|
||||||
|
nc.publish(reply, "world")
|
||||||
|
end
|
||||||
|
|
||||||
|
EM.add_periodic_timer(1) do
|
||||||
|
puts "[#{Time.now}] Saying hi (servers in pool: #{nc.server_pool}"
|
||||||
|
nc.publish("hello", "hi", inbox)
|
||||||
|
end
|
||||||
|
end'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 3:
|
||||||
|
Now you can add more nodes to the Swarm cluster via more docker services, referencing the seed server in the `-routes` parameter:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service create --network nats-cluster-example --name nats-cluster-node-2 nats:1.0.0 -cluster nats://0.0.0.0:6222 -routes nats://nats-cluster-node-1:6222 -DV
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, `nats-cluster-node-1` is seeding the rest of the cluster through the autodiscovery feature. Now NATS servers `nats-cluster-node-1` and `nats-cluster-node-2` are clustered together.
|
||||||
|
|
||||||
|
Add in more replicas of the subscriber:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service scale ruby-nats=3
|
||||||
|
```
|
||||||
|
|
||||||
|
Then confirm the distribution on the Docker Swarm cluster:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service ps ruby-nats
|
||||||
|
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
|
||||||
|
25skxso8honyhuznu15e4989m ruby-nats.1 wallyqs/ruby-nats:ruby-2.3.1-nats-v0.8.0 node-1 Running Running 2 minutes ago
|
||||||
|
0017lut0u3wj153yvp0uxr8yo ruby-nats.2 wallyqs/ruby-nats:ruby-2.3.1-nats-v0.8.0 node-1 Running Running 2 minutes ago
|
||||||
|
2sxl8rw6vm99x622efbdmkb96 ruby-nats.3 wallyqs/ruby-nats:ruby-2.3.1-nats-v0.8.0 node-2 Running Running 2 minutes ago
|
||||||
|
```
|
||||||
|
|
||||||
|
The sample output after adding more NATS server nodes to the cluster, is below - and notice that the client is *dynamically* aware of more nodes being part of the cluster via auto discovery!
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[2016-08-15 12:51:52 +0000] Saying hi (servers in pool: [{:uri=>#<URI::Generic nats://10.0.1.3:4222>, :was_connected=>true, :reconnect_attempts=>0}]
|
||||||
|
[2016-08-15 12:51:53 +0000] Saying hi (servers in pool: [{:uri=>#<URI::Generic nats://10.0.1.3:4222>, :was_connected=>true, :reconnect_attempts=>0}]
|
||||||
|
[2016-08-15 12:51:54 +0000] Saying hi (servers in pool: [{:uri=>#<URI::Generic nats://10.0.1.3:4222>, :was_connected=>true, :reconnect_attempts=>0}]
|
||||||
|
[2016-08-15 12:51:55 +0000] Saying hi (servers in pool: [{:uri=>#<URI::Generic nats://10.0.1.3:4222>, :was_connected=>true, :reconnect_attempts=>0}, {:uri=>#<URI::Generic nats://10.0.1.7:4222>, :reconnect_attempts=>0}, {:uri=>#<URI::Generic nats://10.0.1.6:4222>, :reconnect_attempts=>0}]
|
||||||
|
```
|
||||||
|
|
||||||
|
Sample output after adding more workers which can reply back (since ignoring own responses):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
[2016-08-15 16:06:26 +0000] Received reply - world
|
||||||
|
[2016-08-15 16:06:26 +0000] Received reply - world
|
||||||
|
[2016-08-15 16:06:27 +0000] Received greeting - hi - _INBOX.b8d8c01753d78e562e4dc561f1
|
||||||
|
[2016-08-15 16:06:27 +0000] Received greeting - hi - _INBOX.4c35d18701979f8c8ed7e5f6ea
|
||||||
|
```
|
||||||
|
|
||||||
|
### And so forth...
|
||||||
|
From here you can experiment adding to the NATS cluster by simply adding servers with new service names, that route to the seed server `nats-cluster-node-1`. As you've seen above, clients will automatically be updated to know that new servers are available in the cluster.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
% docker service create --network nats-cluster-example --name nats-cluster-node-3 nats:1.0.0 -cluster nats://0.0.0.0:6222 -routes nats://nats-cluster-node-1:6222 -DV
|
||||||
|
```
|
@ -2,12 +2,37 @@
|
|||||||
|
|
||||||
The NATS server provides various ways of authenticating clients:
|
The NATS server provides various ways of authenticating clients:
|
||||||
|
|
||||||
- Token Authentication
|
- [Token Authentication](tokens.md)
|
||||||
- Username/Password credentials
|
- [Username/Password credentials](username_password.md)
|
||||||
- TLS Certificate
|
- [TLS Certificate](tls_mutual_auth.md)
|
||||||
- NKEY with Challenge
|
- [NKEY with Challenge](nkey_auth.md)
|
||||||
- JWTs with Challenge
|
- [JWTs with Challenge](jwt_auth.md)
|
||||||
|
|
||||||
Authentication deals with allowing a NATS client to connect to the server.
|
Authentication deals with allowing a NATS client to connect to the server.
|
||||||
With the exception of JWT authentication, authentication and authorization configuration is in the `authorization` block of the configuration.
|
With the exception of JWT authentication, authentication and authorization is configured in the `authorization` section of the configuration.
|
||||||
|
|
||||||
|
## Authorization Map
|
||||||
|
|
||||||
|
The `authorization` block provides _authentication_ configuration as well as _authorization_:
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| [`token`](tokens.md) | Specifies a global token that can be used to authenticate to the server (exclusive of user and password) |
|
||||||
|
| [`user`](username_password.md) | Specifies a single _global_ user name for clients to the server (exclusive of token) |
|
||||||
|
| [`password`](username_password.md) | Specifies a single _global_ password for clients to the server (exclusive of `token`) |
|
||||||
|
| `users` | A list of `user` configuration maps |
|
||||||
|
| `timeout` | Maximum number of seconds to wait for client authentication |
|
||||||
|
|
||||||
|
For multiple username and password credentials, specify a `users` list.
|
||||||
|
|
||||||
|
|
||||||
|
### User Configuration Map
|
||||||
|
|
||||||
|
A `user` configuration map specifies credentials and permissions options for a single user:
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| [`user`](username_password.md) | username for client authentication |
|
||||||
|
| [`password`](username_password.md) | password for the user entry |
|
||||||
|
| [`nkey`](nkey_auth.md) | public nkey identifying an user |
|
||||||
|
| [`permissions`](authorization.md) | permissions map configuring subjects accessible to the user |
|
||||||
|
19
nats_server/auth_timeout.md
Normal file
19
nats_server/auth_timeout.md
Normal file
@ -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}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
@ -1,25 +1,37 @@
|
|||||||
## Authorization
|
## Authorization
|
||||||
|
|
||||||
The NATS server supports authorization using subject-level permissions on a per-user basis. Permission-based authorization is available with [multi-user authentication](/documentation/managing_the_server/authentication/).
|
|
||||||
|
|
||||||
Each permission grant is an object with two fields: what subject(s) the authenticated user can publish to, and what subject(s) the authenticated user can subscribe to. The parser is generous at understanding what the intent is, so both arrays and singletons are processed. Subjects themselves can contain wildcards. Permissions can make use of [variables](/documentation/managing_the_server/configuration).
|
The NATS server supports authorization using subject-level permissions on a per-user basis. Permission-based authorization is available withmulti-user authentication via the `users` list.
|
||||||
|
|
||||||
|
Each permission specifies the subjects the user can publish to and subscribe to. The parser is generous at understanding what the intent is, so both arrays and singletons are processed. For more complex configuation you can specify a `permission` object which explicetly allows or denies subjects. The specified subjects can specify wildcards. Permissions can make use of [variables](configuration.md#variables).
|
||||||
|
|
||||||
|
You configure authorization by creating a `permissions` entry in the `authorization` object.
|
||||||
|
|
||||||
|
### Permissions Configuration Map
|
||||||
|
|
||||||
|
The `permissions` map specify subjects that can be subscribed to or published by the specified client.
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `publish` | subject, list of subjects, or permission map the client can publish |
|
||||||
|
| `subscribe` | subject, list of subjects, or permission map the client can publish |
|
||||||
|
|
||||||
|
### Permission Map
|
||||||
|
|
||||||
|
The `permission` map provides additional properties for configuring a `permissions` map. Instead of providing a list of subjects that are allowed, the `permission` map allows you to explicitely list subjects you want to`allow` or `deny`:
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `allow` | List of subject names that are allowed to the client |
|
||||||
|
| `deny` | List of subjects that are denied to the client |
|
||||||
|
|
||||||
You set permissions by creating an entry inside of the `authorization` configuration block that conforms to the following syntax:
|
|
||||||
|
|
||||||
```ascii
|
|
||||||
authorization {
|
|
||||||
PERMISSION_NAME = {
|
|
||||||
publish = "singleton" or ["array", ...]
|
|
||||||
subscribe = "singleton" or ["array", ...]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important Note** NATS Authorizations are whitelist only, meaning in order to not break request/reply patterns you need to add rules as above with Alice and Bob for the `_INBOX.>` pattern. If an unauthorized client publishes or attempts to subscribe to a subject that has not been whitelisted, the action fails and is logged at the server, and an error message is returned to the client.
|
**Important Note** NATS Authorizations are whitelist only, meaning in order to not break request/reply patterns you need to add rules as above with Alice and Bob for the `_INBOX.>` pattern. If an unauthorized client publishes or attempts to subscribe to a subject that has not been whitelisted, the action fails and is logged at the server, and an error message is returned to the client.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
Here is an example authorization configuration that defines four users, three of whom are assigned explicit permissions.
|
Here is an example authorization configuration that uses _variables_ which defines four users, three of whom are assigned explicit permissions.
|
||||||
|
|
||||||
```ascii
|
```ascii
|
||||||
authorization {
|
authorization {
|
||||||
@ -28,31 +40,34 @@ authorization {
|
|||||||
subscribe = ">"
|
subscribe = ">"
|
||||||
}
|
}
|
||||||
REQUESTOR = {
|
REQUESTOR = {
|
||||||
publish = ["req.foo", "req.bar"]
|
publish = ["req.a", "req.b"]
|
||||||
subscribe = "_INBOX.>"
|
subscribe = "_INBOX.>"
|
||||||
}
|
}
|
||||||
RESPONDER = {
|
RESPONDER = {
|
||||||
subscribe = ["req.foo", "req.bar"]
|
subscribe = ["req.a", "req.b"]
|
||||||
publish = "_INBOX.>"
|
publish = "_INBOX.>"
|
||||||
}
|
}
|
||||||
DEFAULT_PERMISSIONS = {
|
DEFAULT_PERMISSIONS = {
|
||||||
publish = "SANDBOX.*"
|
publish = "SANDBOX.*"
|
||||||
subscribe = ["PUBLIC.>", "_INBOX.>"]
|
subscribe = ["PUBLIC.>", "_INBOX.>"]
|
||||||
}
|
}
|
||||||
PASS: abcdefghijklmnopqrstuvwxwz0123456789
|
|
||||||
users = [
|
users = [
|
||||||
{user: joe, password: foo, permissions: $ADMIN}
|
{user: admin, password: $ADMIN_PASS, permissions: $ADMIN}
|
||||||
{user: alice, password: bar, permissions: $REQUESTOR}
|
{user: client, password: $CLIENT_PASS, permissions: $REQUESTOR}
|
||||||
{user: bob, password: $PASS, permissions: $RESPONDER}
|
{user: service, password: $SERVICE_PASS, permissions: $RESPONDER}
|
||||||
{user: charlie, password: bar}
|
{user: other, password: $OTHER_PASS}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Since Joe is an ADMIN he can publish/subscribe on any subject. We use the wildcard `>` to match any subject.
|
> *DEFAULT_PERMISSIONS* is a special permissions name. If defined, it applies to all users that don't have specific permissions set.
|
||||||
|
|
||||||
Alice is a REQUESTOR and can publish requests on subjects `req.foo` or `req.bar`, and subscribe to anything that is a response (`_INBOX.>`).
|
- _admin_ has `ADMIN` permissions and can publish/subscribe on any subject. We use the wildcard `>` to match any subject.
|
||||||
|
|
||||||
Charlie has no permissions granted and therefore inherits the default permission set. You set the inherited default permissions by assigning them to the default_permissions entry inside of the authorization configuration block.
|
- _client_ is a `REQUESTOR` and can publish requests on subjects `req.a` or `req.b`, and subscribe to anything that is a response (`_INBOX.>`).
|
||||||
|
|
||||||
Bob is a RESPONDER to any of Alice's requests, so Bob needs to be able to subscribe to the request subjects and respond to Alice's reply subject which will be an `_INBOX.>`.
|
- _service_ is a `RESPONDER` to `req.a` and `req.b` requests, so it needs to be able to subscribe to the request subjects and respond to client's that are able to publish requests to `req.a` and `req.b`. The reply subject subject is an inbox. Typically inboxes start with the prefix `_INBOX.` followed by a generated string. The `_INBOX.>` subject matches all subjects that start with `_INBOX.`.
|
||||||
|
|
||||||
|
- _other_ has no permissions granted and therefore inherits the default permission set. You set the inherited default permissions by assigning them to the `default_permissions` entry inside of the authorization configuration block.
|
||||||
|
|
||||||
|
> Note that in the above example, any client with permissions to subscribe to `_INBOX.>` is able to receive _all_ responses published. More sensitive installations will want to add or subset the prefix to further limit what a client can subscribe to. Alternatively [_Accounts_](jwt_auth.md) allow complete isolation limiting what members of an account can see.
|
||||||
|
34
nats_server/cluster_config.md
Normal file
34
nats_server/cluster_config.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
## Cluster Configuration
|
||||||
|
|
||||||
|
The `cluster` configuration map has the following configuration options:
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| `listen` | host/port for inbound route connections |
|
||||||
|
| `authorization` | [authorization](authorization.md) map for configuring cluster clients. Supports `token`, `username`/`password` and `TLS authentication`. `permissions` are ignored. |
|
||||||
|
| `timeout` | Maximum amount of time (in seconds) to wait for a clustering connection to complete
|
||||||
|
| `tls` | A [`tls` configuration map](tls.md#tls-configuration) for securing the clustering connection |
|
||||||
|
| `routes` | A list of other servers (URLs) to cluster with. Self-routes are ignored. |
|
||||||
|
|
||||||
|
|
||||||
|
```ascii
|
||||||
|
cluster {
|
||||||
|
listen: localhost:4244 # host/port for inbound route connections
|
||||||
|
|
||||||
|
# Authorization for route connections
|
||||||
|
authorization {
|
||||||
|
user: route_user
|
||||||
|
# ./util/mkpasswd -p T0pS3cr3tT00!
|
||||||
|
password: $2a$11$xH8dkGrty1cBNtZjhPeWJewu/YPbSU.rXJWmS6SFilOBXzmZoMk9m
|
||||||
|
timeout: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
# Routes are actively solicited and connected to from this server.
|
||||||
|
# Other servers can connect to us if they supply the correct credentials
|
||||||
|
# in their routes definitions from above.
|
||||||
|
routes = [
|
||||||
|
nats-route://user1:pass1@127.0.0.1:4245
|
||||||
|
nats-route://user2:pass2@127.0.0.1:4246
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
@ -9,8 +9,8 @@ The NATS configuration file supports the following syntax:
|
|||||||
- Equals sign: `foo = 2`
|
- Equals sign: `foo = 2`
|
||||||
- Colon: `foo: 2`
|
- Colon: `foo: 2`
|
||||||
- Whitespace: `foo 2`
|
- Whitespace: `foo 2`
|
||||||
- Arrays are enclosed in brackets: `[...]`
|
- Arrays are enclosed in brackets: `["a", "b", "c"]`
|
||||||
- Maps are enclosed in braces: `{...}`
|
- Maps are enclosed in braces: `{foo: 2}`
|
||||||
- Maps can be assigned with no key separator
|
- Maps can be assigned with no key separator
|
||||||
- Semicolons can be used as terminators
|
- Semicolons can be used as terminators
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ Server configurations can specify variables. Variables allow you to reference a
|
|||||||
Variables:
|
Variables:
|
||||||
- Are block scoped
|
- Are block scoped
|
||||||
- Are referenced with a `$` prefix.
|
- Are referenced with a `$` prefix.
|
||||||
- Can be resolved from the environment variables having the same name
|
- Can be resolved from environment variables having the same name
|
||||||
|
|
||||||
> If the environment variable value begins with a number you may have trouble resolving it depending on the server version you are running.
|
> If the environment variable value begins with a number you may have trouble resolving it depending on the server version you are running.
|
||||||
|
|
||||||
@ -97,6 +97,30 @@ authorization: {
|
|||||||
> nats-server -c server.conf
|
> nats-server -c server.conf
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configuration Properties
|
||||||
|
|
||||||
|
| Property | Description |
|
||||||
|
| :------ | :---- |
|
||||||
|
| [`authorization`](auth_intro.md) | configuration map for client authentication/authorization |
|
||||||
|
| [`cluster`](cluster_config.md) | configuration map for clustering configuration |
|
||||||
|
| `debug` | if `true` enable debug log messages |
|
||||||
|
| [`gateway`](/gateways/gateway.md) | Gateway configuration map |
|
||||||
|
| `host` | host for client connections |
|
||||||
|
| [`http_port`](monitoring.md) | http port for server monitoring |
|
||||||
|
| [`https_port`](monitoring.md) | https port for server monitoring |
|
||||||
|
| [`leafnode`](/leafnodes/leafnode_conf.md) | Leafnode configuration map |
|
||||||
|
| `listen` | host/port for client connections |
|
||||||
|
| `max_connections` | Maximum number of active client connections |
|
||||||
|
| `max_control_line` | Maximum length of a protocol line (including subject length) |
|
||||||
|
| `max_payload` | Maximum number of bytes in a message payload |
|
||||||
|
| `max_subscriptions` | Maximum numbers of subscriptions for a client connection |
|
||||||
|
| [`operator`](/nats_tools/nsc/nsc.md#nats-server-configuration) | path to an operator JWT |
|
||||||
|
| `port` | port for client connections |
|
||||||
|
| [`resolver`](/nats_tools/nsc/nsc.md#nats-server-configuration) | Resolver type `MEMORY` or `URL` for account JWTs |
|
||||||
|
| [`tls`](tls.md#tls-configuration) | configuration map for tls for client and http monitoring |
|
||||||
|
| `trace` | if `true` enable protocol trace log messages |
|
||||||
|
| `write_deadline` | Maximum number of seconds the server will block when writing a to a client (slow consumer) |
|
||||||
|
|
||||||
|
|
||||||
### Configuration Reloading
|
### Configuration Reloading
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
|
|
||||||
The NATS server has many flags to customize it's behaviour without having to write a configuration file.
|
The NATS server has many flags to customize its behaviour without having to write a configuration file.
|
||||||
|
|
||||||
The configuration flags revolve around:
|
The configuration flags revolve around:
|
||||||
|
|
||||||
|
93
nats_server/jwt_auth.md
Normal file
93
nats_server/jwt_auth.md
Normal file
@ -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.
|
@ -1,18 +1,13 @@
|
|||||||
## NKey Authentication
|
## 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.
|
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.
|
> 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
|
### Generating NKeys and Configuring the Server
|
||||||
|
|
||||||
@ -28,7 +23,7 @@ The first output line starts with the letter `S` for _Seed_. The second letter `
|
|||||||
|
|
||||||
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
|
```text
|
||||||
authorization: {
|
authorization: {
|
||||||
|
@ -23,7 +23,7 @@ When the server starts it will print some information including where the server
|
|||||||
If you are running your NATS server in a docker container:
|
If you are running your NATS server in a docker container:
|
||||||
|
|
||||||
```
|
```
|
||||||
> docker run -p 4222:4222 -ti nats-server:latest
|
> docker run -p 4222:4222 -ti nats:latest
|
||||||
[1] 2019/05/13 14:55:11.981434 [INF] Starting nats-server version 2.0.0
|
[1] 2019/05/13 14:55:11.981434 [INF] Starting nats-server version 2.0.0
|
||||||
...
|
...
|
||||||
[1] 2019/05/13 14:55:11.981545 [INF] Starting http monitor on 0.0.0.0:8222
|
[1] 2019/05/13 14:55:11.981545 [INF] Starting http monitor on 0.0.0.0:8222
|
||||||
|
@ -3,12 +3,17 @@
|
|||||||
The NATS server uses modern TLS semantics to encrypt client, route and monitoring connections.
|
The NATS server uses modern TLS semantics to encrypt client, route and monitoring connections.
|
||||||
Server configuration revolves around a `tls` map, which has the following properties:
|
Server configuration revolves around a `tls` map, which has the following properties:
|
||||||
|
|
||||||
- `cert_file` - the server's certificate
|
| Property | Description |
|
||||||
- `key_file` - the server's key file
|
| :------ | :---- |
|
||||||
- `ca_file` - a certificate file providing the trust chain for the certificate authority (CA). Used to validate client certificates.
|
| `ca_file` | TLS certificate authority file. |
|
||||||
- `timeout` - max seconds to allow for a TLS connection upgrade (default is 2 seconds)
|
| `cert_file` | TLS certificate file. |
|
||||||
- `verify` - set to `true` if you want to verify client certs against the `ca_file` certificate.
|
| `cipher_suites` | When set, only the specified TLS cipher suites will be allowed. Values must match golang version used to build the server. |
|
||||||
|
| `curve_preferences` | List of TLS cypher curves to use in order. |
|
||||||
|
| `insecure` | Skip certificate verfication. |
|
||||||
|
| `key_file` | TLS certificate key file. |
|
||||||
|
| `timeout` | TLS handshake timeout in fractional seconds. |
|
||||||
|
| `verify_and_map` | If `true`, require and verify client certificates and map certificate values for authentication purposes. |
|
||||||
|
| `verify` | If `true`, require and verify client certificates. |
|
||||||
|
|
||||||
The simplest configuration:
|
The simplest configuration:
|
||||||
```
|
```
|
||||||
|
@ -26,7 +26,7 @@ Or via the command line:
|
|||||||
|
|
||||||
This option simply verifies the client's certificate has been signed by the CA specified in the `ca_file` option.
|
This option simply verifies the client's certificate has been signed by the CA specified in the `ca_file` option.
|
||||||
|
|
||||||
## Mapping Client Certificates To An User
|
## Mapping Client Certificates To A User
|
||||||
|
|
||||||
In addition to verifying that a client certificate was issued by a specified CA, you can use information encoded in the certificate to authenticate a client. The client wouldn't have to provide or track usernames or passwords.
|
In addition to verifying that a client certificate was issued by a specified CA, you can use information encoded in the certificate to authenticate a client. The client wouldn't have to provide or track usernames or passwords.
|
||||||
|
|
||||||
@ -90,3 +90,7 @@ authorization {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## TLS Timeout
|
||||||
|
|
||||||
|
[TLS timeout](/nats_server/tls.md#tls-timeout) is described here.
|
@ -42,7 +42,7 @@ authorization {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The client will still require the clear-text password to connect:
|
The client will still require the clear-text token to connect:
|
||||||
|
|
||||||
```
|
```
|
||||||
nats-sub -s nats://dag0HTXl4RGg7dXdaJwbC8@localhost:4222 ">"
|
nats-sub -s nats://dag0HTXl4RGg7dXdaJwbC8@localhost:4222 ">"
|
||||||
|
@ -37,13 +37,14 @@ bcrypt hash: $2a$11$V1qrpBt8/SLfEBr4NJq4T.2mg8chx8.MTblUiTBOLV3MKDeAy.f7u
|
|||||||
```
|
```
|
||||||
And on the configuration file:
|
And on the configuration file:
|
||||||
|
|
||||||
|
```
|
||||||
authorization: {
|
authorization: {
|
||||||
users: [
|
users: [
|
||||||
{user: a, password: "$2a$11$V1qrpBt8/SLfEBr4NJq4T.2mg8chx8.MTblUiTBOLV3MKDeAy.f7u"},
|
{user: a, password: "$2a$11$V1qrpBt8/SLfEBr4NJq4T.2mg8chx8.MTblUiTBOLV3MKDeAy.f7u"},
|
||||||
...
|
...
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Reloading a Configuration
|
## Reloading a Configuration
|
||||||
|
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
## NATS Tools
|
## NATS Tools
|
||||||
|
|
||||||
The NATS Ecosystem has many tools, some interesting tools include:
|
The NATS Ecosystem has many tools to support server configuration, enhance monitoring or tune performance:
|
||||||
|
|
||||||
- [NATS TOP (statistics)](nats_top/README.md)
|
- [mkpasswd](nats_tools/mkpasswd.md) - Generates or bcrypts passwords
|
||||||
|
- [nk](nats_tools/nk.md) - Generate NKeys
|
||||||
|
- [nsc](nats_tools/nsc/README.md) - Configure Operators, Accounts and Users
|
||||||
|
- [nats account server](nats_tools/nas/README.md) - Serve Account JWTs
|
||||||
|
- [nats top](nats_tools/nats_top/README.md) - Monitor NATS Server
|
||||||
|
- [nats-bench](nats_tools/natsbench.md) - Benchmark NATS Server
|
16
nats_tools/nas/README.md
Normal file
16
nats_tools/nas/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
## NATS Account Server
|
||||||
|
|
||||||
|
The [NATS Account Server](https://github.com/nats-io/nats-account-server) is an HTTP server that hosts and vends JWTs for nats-server 2.0 account authentication. The server supports an number of stores which enable it to serve JWTs from:
|
||||||
|
|
||||||
|
- a directory
|
||||||
|
- an [NSC](../nsc/nsc.md) directory
|
||||||
|
- memory (for testing purposes)
|
||||||
|
|
||||||
|
The server can operate in a _READ ONLY_ mode where it serves content from a directory, or in notification mode, where it can notify a NATS server that JWT in the store have been modified, updating the NATS server with the updated JWT.
|
||||||
|
|
||||||
|
|
||||||
|
### Memory Resolver
|
||||||
|
|
||||||
|
For very simple installations, where JWTs are mostly static, the NATS server also supports a _Memory Resolver_ that can be configured statically in the server's configuration file.
|
||||||
|
|
||||||
|
You can learn more about how to configure the [memory resolver here](mem_resolver.md).
|
24
nats_tools/nas/dir_store.md
Normal file
24
nats_tools/nas/dir_store.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
## Directory Store
|
||||||
|
|
||||||
|
|
||||||
|
### NATS Account Server Configuration
|
||||||
|
|
||||||
|
```
|
||||||
|
OperatorJWTPath: "/users/synadia/.nsc/nats/Test/Test.jwt",
|
||||||
|
http {
|
||||||
|
port: 9090
|
||||||
|
},
|
||||||
|
store {
|
||||||
|
dir: "/tmp/as_store",
|
||||||
|
readonly: false,
|
||||||
|
shard: true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### To Add/Update a JWT
|
||||||
|
|
||||||
|
```
|
||||||
|
> curl -i -X POST localhost:9090/jwt/v1/accounts/AC7PO3MREV26U3LFZFP5BN3HAI32X3PKLBRVMPAETLEHWPQEUG7EJY4H --data-binary @/Users/synadia/.nsc/nats/Test/accounts/TestAccount/TestAccount.jwt -H "Content-Type: text/text"
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `@` before the file name is required for curl to read the specified file, and use it as the payload. Otherwise it will simply post the path specified, which will result in an update error.
|
40
nats_tools/nas/inspecting_jwts.md
Normal file
40
nats_tools/nas/inspecting_jwts.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
## Inspecting a JWT from the `nats-account-server`
|
||||||
|
|
||||||
|
Let’s say that you know the account for a stream that you are interested in, but you don't know all the details for creating an import. If you know and have access to a nats-account-server, you can help yourself. The nats-account-server can decode a JWT and give you human readable values that you can use.
|
||||||
|
|
||||||
|
The endpoint for retrieving an account JWT is:
|
||||||
|
`/jwt/v1/accounts/<account_id>`. To decode a JWT add the query string `?decode=true`.
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
> curl http://localhost:9090/jwt/v1/accounts/AC7PO3MREV26U3LFZFP5BN3HAI32X3PKLBRVMPAETLEHWPQEUG7EJY4H\?decode=true
|
||||||
|
{
|
||||||
|
"typ": "jwt",
|
||||||
|
"alg": "ed25519"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"jti": "5YMRO4KNMYWQDMRAHVTT4KX63CA2L3M6F4VM3S7NNGPMCCATORXQ",
|
||||||
|
"iat": 1556229062 (2019-04-25),
|
||||||
|
"iss": "OAYI3YUZSWDNMERD2IN3HZSIP3JA2E3VDTXSTEVOIII273XL2NABJP64",
|
||||||
|
"name": "TestAccount",
|
||||||
|
"sub": "AC7PO3MREV26U3LFZFP5BN3HAI32X3PKLBRVMPAETLEHWPQEUG7EJY4H",
|
||||||
|
"type": "account",
|
||||||
|
"nats": {
|
||||||
|
"exports": [
|
||||||
|
{
|
||||||
|
"name": "abc",
|
||||||
|
"subject": "a.b.c.>",
|
||||||
|
"type": "stream"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
…
|
||||||
|
```
|
||||||
|
As you can see from above, the JWT is decoded. The standard JWT claim field abbreviated names may be a little terse, so here's a list of the more important ones:
|
||||||
|
|
||||||
|
- `jti` is the _JWT ID_. All JWTs have one and they are unique.
|
||||||
|
- `iat` is _Issued At_ - the UNIX date (number of seconds since 1970) when the JWT was issued.
|
||||||
|
- `iss` is the _Issuer_. For NATS JWTs it is the public key of the issuer. In the example above the entity is an account, so the issuer will be an operator. Thus the id will always start with the letter `O`.
|
||||||
|
- `sub` is the _Subject_ of the claim. In NATS JWTs it is the public key of the entity of the claim is for. In the example above, it is an Account, so the issuer will always start with the letter `A`.
|
||||||
|
|
||||||
|
|
||||||
|
On the example above, we see that there is one export in this account, it is public (`token_req` is `false` or not set), and it is a `stream`. So this account exports a public stream. With that information you can create an import on the public stream.
|
78
nats_tools/nas/mem_resolver.md
Normal file
78
nats_tools/nas/mem_resolver.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
## Memory Resolver
|
||||||
|
|
||||||
|
The `MEMORY` resolver is a built-in resolver for JWTs. It is mostly used by test setups but can be used to test the simplest of environments where there is one or very few accounts, and the account JWTs don’t change often.
|
||||||
|
|
||||||
|
The basic configuration for the server requires:
|
||||||
|
|
||||||
|
- The operator JWT
|
||||||
|
- `resolver` set to `MEMORY`
|
||||||
|
- `resolver_preload` set to an object where account public keys are mapped to account JWTs.
|
||||||
|
|
||||||
|
|
||||||
|
### Create Required Entities
|
||||||
|
|
||||||
|
Let’s create the setup:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nsc add operator -n memory
|
||||||
|
Generated operator key - private key stored "/Users/synadia/.nkeys/memory/memory.nk"
|
||||||
|
Success! - added operator "memory"
|
||||||
|
|
||||||
|
> nsc add account --name A
|
||||||
|
Generated account key - private key stored "/Users/synadia/.nkeys/memory/accounts/A/A.nk"
|
||||||
|
Success! - added account "A"
|
||||||
|
|
||||||
|
> nsc describe account -W
|
||||||
|
╭──────────────────────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Account Details │
|
||||||
|
├───────────────────────────┬──────────────────────────────────────────────────────────┤
|
||||||
|
│ Name │ A │
|
||||||
|
│ Account ID │ ACSU3Q6LTLBVLGAQUONAGXJHVNWGSKKAUA7IY5TB4Z7PLEKSR5O6JTGR │
|
||||||
|
│ Issuer ID │ ODWZJ2KAPF76WOWMPCJF6BY4QIPLTUIY4JIBLU4K3YDG3GHIWBVWBHUZ │
|
||||||
|
│ Issued │ 2019-04-30 20:21:34 UTC │
|
||||||
|
│ Expires │ │
|
||||||
|
├───────────────────────────┼──────────────────────────────────────────────────────────┤
|
||||||
|
│ 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 │
|
||||||
|
╰───────────────────────────┴──────────────────────────────────────────────────────────╯
|
||||||
|
|
||||||
|
> cat /Users/synadia/.nsc/nats/memory/accounts/A/A.jwt
|
||||||
|
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJPRFhJSVI2Wlg1Q1AzMlFJTFczWFBENEtTSDYzUFNNSEZHUkpaT05DR1RLVVBISlRLQ0JBIiwiaWF0IjoxNTU2NjU1Njk0LCJpc3MiOiJPRFdaSjJLQVBGNzZXT1dNUENKRjZCWTRRSVBMVFVJWTRKSUJMVTRLM1lERzNHSElXQlZXQkhVWiIsIm5hbWUiOiJBIiwic3ViIjoiQUNTVTNRNkxUTEJWTEdBUVVPTkFHWEpIVk5XR1NLS0FVQTdJWTVUQjRaN1BMRUtTUjVPNkpUR1IiLCJ0eXBlIjoiYWNjb3VudCIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJjb25uIjotMSwibGVhZiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0._WW5C1triCh8a4jhyBxEZZP8RJ17pINS8qLzz-01o6zbz1uZfTOJGvwSTS6Yv2_849B9iUXSd-8kp1iMXHdoBA
|
||||||
|
|
||||||
|
> nsc add user --name TA
|
||||||
|
Generated user key - private key stored “/Users/synadia/.nkeys/memory/accounts/A/users/TA.nk"
|
||||||
|
Generated user creds file “/Users/synadia/.nkeys/memory/accounts/A/users/TA.creds"
|
||||||
|
Success! - added user "TA" to "A"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create the Server Config
|
||||||
|
|
||||||
|
With the above entries, we can reference the operator JWT and the account JWT in a server configuration. Remember that your configuration will be in `$NSC_HOME/nats/<operator_name>/<operator_name>.jwt` for the operator. The account JWT will be in `$NSC_HOME/nats/<operator_name>/accounts/<account_name>/<account_name>.jwt`
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator: /Users/synadia/.nsc/nats/memory/memory.jwt
|
||||||
|
resolver: MEMORY
|
||||||
|
resolver_preload: {
|
||||||
|
ACSU3Q6LTLBVLGAQUONAGXJHVNWGSKKAUA7IY5TB4Z7PLEKSR5O6JTGR: eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJPRFhJSVI2Wlg1Q1AzMlFJTFczWFBENEtTSDYzUFNNSEZHUkpaT05DR1RLVVBISlRLQ0JBIiwiaWF0IjoxNTU2NjU1Njk0LCJpc3MiOiJPRFdaSjJLQVBGNzZXT1dNUENKRjZCWTRRSVBMVFVJWTRKSUJMVTRLM1lERzNHSElXQlZXQkhVWiIsIm5hbWUiOiJBIiwic3ViIjoiQUNTVTNRNkxUTEJWTEdBUVVPTkFHWEpIVk5XR1NLS0FVQTdJWTVUQjRaN1BMRUtTUjVPNkpUR1IiLCJ0eXBlIjoiYWNjb3VudCIsIm5hdHMiOnsibGltaXRzIjp7InN1YnMiOi0xLCJjb25uIjotMSwibGVhZiI6LTEsImltcG9ydHMiOi0xLCJleHBvcnRzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ3aWxkY2FyZHMiOnRydWV9fX0._WW5C1triCh8a4jhyBxEZZP8RJ17pINS8qLzz-01o6zbz1uZfTOJGvwSTS6Yv2_849B9iUXSd-8kp1iMXHdoBA
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Save the config at server.conf and start the server:
|
||||||
|
```text
|
||||||
|
> nats-server -c server.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
Start a subscriber:
|
||||||
|
```text
|
||||||
|
> nats-sub -creds /Users/synadia/.nkeys/memory/accounts/A/users/TA.creds ">"
|
||||||
|
Listening on [>]
|
||||||
|
```
|
164
nats_tools/nas/nas_conf.md
Normal file
164
nats_tools/nas/nas_conf.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
## Configuration
|
||||||
|
|
||||||
|
Basic configuration revolves around 4 settings:
|
||||||
|
|
||||||
|
- The store to read JWTs from
|
||||||
|
- The HTTP/S configuration
|
||||||
|
- NATS (for cases where updates are enabled)
|
||||||
|
- Logging
|
||||||
|
|
||||||
|
For complete information on please refer to the project's [Github](https://github.com/nats-io/nats-account-server).
|
||||||
|
|
||||||
|
|
||||||
|
### `nsc` Configuration
|
||||||
|
|
||||||
|
For a basic usage of the server you can specify the `-nsc` flag, and specify the path to an operator in your environment.
|
||||||
|
|
||||||
|
> If you have not yet created an operator or accounts, you'll need to do so before continuing. See [NSC](../nsc/README.md)
|
||||||
|
|
||||||
|
You can easily locate the path by running `nsc env` to print your `nsc` settings:
|
||||||
|
|
||||||
|
```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 │ │ Test │
|
||||||
|
│ Default Account │ │ TestAccount │
|
||||||
|
│ Default Cluster │ │ │
|
||||||
|
╰──────────────────┴─────┴─────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
The path you are interested in the `Stores Dir`. This is the root of all operators, you'll also need the name of your operator. If your current operator is not listed, you can list all your available operators by doing:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nsc list operators
|
||||||
|
```
|
||||||
|
|
||||||
|
To start the `nats-account-server` with the operator `Test`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nats-account-server -nsc ~/.nsc/nats/Test
|
||||||
|
2019/05/10 11:22:41.845148 [INF] starting NATS Account server, version 0.0-dev
|
||||||
|
2019/05/10 11:22:41.845241 [INF] server time is Fri May 10 11:22:41 CDT 2019
|
||||||
|
2019/05/10 11:22:41.845245 [INF] loading operator from /Users/synadia/.nsc/nats/Test/Test.jwt
|
||||||
|
2019/05/10 11:22:41.846367 [INF] creating a read-only store for the NSC folder at /Users/synadia/.nsc/nats/Test
|
||||||
|
2019/05/10 11:22:41.846459 [INF] NATS is not configured, server will not fire notifications on update
|
||||||
|
2019/05/10 11:22:41.855291 [INF] http listening on port 9090
|
||||||
|
2019/05/10 11:22:41.855301 [INF] nats-account-server is running
|
||||||
|
2019/05/10 11:22:41.855303 [INF] configure the nats-server with:
|
||||||
|
2019/05/10 11:22:41.855306 [INF] resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the server will serve JWTs on the localhost at port 9090. The last line in the shown in the printout is important, that is the resolver URL you'll have to provide on your NATS server configuration. You'll also need the matching operator JWT which is on `~/.nsc/nats/Test/Test.jwt` if you are following the example above. On the server configuration you'll need to expand the `~` as necessary. Here's what my NATS server configuration looks like:
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator: /Users/synadia/.nsc/nats/Test/Test.jwt
|
||||||
|
resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that servers you create with the `-nsc` option (or store option) are read-only. This means that the server will not accept POST requests to update the JWT store.
|
||||||
|
|
||||||
|
### Directory Configuration
|
||||||
|
|
||||||
|
You can start a server using a plain directory. In this case you'll be responsible for adding any JWT that you want resolved.
|
||||||
|
|
||||||
|
> The server looks for account JWTs by using the public key of the account as the file name followed by the extension `.jwt`. The server will not introspect the JWTs, so if you don't name the files correctly, it will fail to find them or serve a JWT that doesn't match the requested account.
|
||||||
|
|
||||||
|
```text
|
||||||
|
> mkdir /tmp/jwts
|
||||||
|
nats-account-server -dir /tmp/jwts
|
||||||
|
2019/05/10 11:33:40.501305 [INF] starting NATS Account server, version 0.0-dev
|
||||||
|
2019/05/10 11:33:40.501383 [INF] server time is Fri May 10 11:33:40 CDT 2019
|
||||||
|
2019/05/10 11:33:40.501404 [INF] creating a store at /tmp/jwts
|
||||||
|
2019/05/10 11:33:40.501430 [INF] NATS is not configured, server will not fire notifications on update
|
||||||
|
2019/05/10 11:33:40.510273 [INF] http listening on port 9090
|
||||||
|
2019/05/10 11:33:40.510283 [INF] nats-account-server is running
|
||||||
|
2019/05/10 11:33:40.510285 [INF] configure the nats-server with:
|
||||||
|
2019/05/10 11:33:40.510291 [INF] resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
```
|
||||||
|
|
||||||
|
Configuration for the NATS server is the same as in the previous example:
|
||||||
|
|
||||||
|
```text
|
||||||
|
operator: /Users/synadia/.nsc/nats/Test/Test.jwt
|
||||||
|
resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration File
|
||||||
|
|
||||||
|
While the `-nsc` and `-dir` store flags are sufficient for some very simple developer setups, any production or non-read-only server will require a configuration file.
|
||||||
|
|
||||||
|
Let's take a look at the configuration options:
|
||||||
|
|
||||||
|
#### Configuration Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `http` | An `http` configuration block specifying HTTP options. |
|
||||||
|
| `logging` | A `logging` configuration block specifying server logging options. |
|
||||||
|
| `nats` | A `nats` configuration block specifying NATS connection information for the account server to push JWT changes to a NATS server. |
|
||||||
|
| `operatorjwtpath` | The path to an operator JWT. Required for non-read-only servers. Only JWTs signed by the operator (or one of it's signing keys) are accepted. |
|
||||||
|
| `store` | A `store` configuration block specifying store options. |
|
||||||
|
| `systemaccountjwtpath` | Path to an Account JWT that should be returned as the system account. |
|
||||||
|
|
||||||
|
|
||||||
|
#### `store` Configuration
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `dir` | Configures a directory as a store. |
|
||||||
|
| `nsc` | Configures an nsc read-only store. The value should be the path to an operator _directory_. Option is mutually exlusive with `dir`. |
|
||||||
|
| `readonly` | If `true`, the store will not accept POST requests. Note that to receive requests, the store must also have `operatorjwtpath` specified as a root option. |
|
||||||
|
| `shard` | If `true`, JWTs are sharded in the store directory. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### `logging` Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `time` | If `true`, a timestamp is added to log messages. |
|
||||||
|
| `debug` | If `true`, debug messages are logged. |
|
||||||
|
| `trace` | If `true`, trace messages are logged. |
|
||||||
|
| `colors` | If `true`, messages are logged using ANSI color escape sequences. |
|
||||||
|
| `pid` | If `true`, the process id for the server is added to log messages. |
|
||||||
|
|
||||||
|
### `http` Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `host` | Interface to listen for requests on. |
|
||||||
|
| `port` | Port to listen for requests on. |
|
||||||
|
| `readtimeout` | Max amount of time in milliseconds to wait for a http read operation to complete. |
|
||||||
|
| `writetimeout` | Max amount of time in milliseconds to wait for a http write operation to complete. |
|
||||||
|
|
||||||
|
|
||||||
|
### `nats` Options
|
||||||
|
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `servers` | List of NATS servers for the account server to use when connecting to a NATS server to publish updates. |
|
||||||
|
| `connecttimeout` | Max amount of time in milliseconds to wait for a NATS connection. |
|
||||||
|
| `reconnecttimewait` | Amount of time in milliseconds to between NATS server reconnect attempts. |
|
||||||
|
| `tls` | A `tls` configuration block. |
|
||||||
|
| `usercredentials` | A credentials _creds_ file for connecting to the NATS server. Account must be a member of a system account. |
|
||||||
|
|
||||||
|
### `tls` Options
|
||||||
|
| Option | Description |
|
||||||
|
| :- | :- |
|
||||||
|
| `root` | filepath to the CA certificate. |
|
||||||
|
| `cert` | filepath to the certificate. |
|
||||||
|
| `cert` | filepath to the certificate key. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
nats_tools/nsc/README.md
Normal file
41
nats_tools/nsc/README.md
Normal file
@ -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)
|
||||||
|
|
240
nats_tools/nsc/nsc.md
Normal file
240
nats_tools/nsc/nsc.md
Normal file
@ -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’
|
||||||
|
```
|
||||||
|
|
293
nats_tools/nsc/services.md
Normal file
293
nats_tools/nsc/services.md
Normal file
@ -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'
|
||||||
|
```
|
152
nats_tools/nsc/signing_keys.md
Normal file
152
nats_tools/nsc/signing_keys.md
Normal file
@ -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_.
|
||||||
|
|
294
nats_tools/nsc/streams.md
Normal file
294
nats_tools/nsc/streams.md
Normal file
@ -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'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
15
sys_accounts/README.md
Normal file
15
sys_accounts/README.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
## System Events
|
||||||
|
|
||||||
|
NATS servers leverage [Account](nats_server/jwt_auth.md) support and generate events such as:
|
||||||
|
|
||||||
|
- account connect/disconnect
|
||||||
|
- authentication errors
|
||||||
|
- server shutdown
|
||||||
|
- server stat summary
|
||||||
|
|
||||||
|
In addition the server supports a limitted number of requests that can be used to query for account connections, server stat summaries, and pinging servers in the cluster.
|
||||||
|
|
||||||
|
These events are only accepted and visible to _system account_ users.
|
||||||
|
|
||||||
|
### The System Events Tutorial
|
||||||
|
You can learn more about System Accounts in the [Tutorial](sys_accounts.md).
|
250
sys_accounts/sys_accounts.md
Normal file
250
sys_accounts/sys_accounts.md
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
## System Events Tutorial
|
||||||
|
|
||||||
|
The following is a short tutorial on how you can activate a system account to:
|
||||||
|
|
||||||
|
- receive periodic updates from the server
|
||||||
|
- send requests to the server
|
||||||
|
- send an account update to the server
|
||||||
|
|
||||||
|
### Events and Services
|
||||||
|
|
||||||
|
The system account publishes messages under well known subject patterns.
|
||||||
|
|
||||||
|
Server initiated events:
|
||||||
|
|
||||||
|
- `$SYS.ACCOUNT.<id>.CONNECT` (client connects)
|
||||||
|
- `$SYS.ACCOUNT.<id>.DISCONNECT` (client disconnects)
|
||||||
|
- `$SYS.SERVER.ACCOUNT.<id>.CONNS` (connections for an account changed)
|
||||||
|
- `$SYS.SERVER.<id>.CLIENT.AUTH.ERR` (authentication error)
|
||||||
|
- `$SYS.ACCOUNT.<id>.LEAFNODE.CONNECT` (leaf node connnects)
|
||||||
|
- `$SYS.SERVER.<id>.STATSZ` (stats summary)
|
||||||
|
|
||||||
|
In addition other tools with system account privileges, can initiate requests:
|
||||||
|
|
||||||
|
- `$SYS.REQ.SERVER.<id>.STATSZ` (request server stat summary)
|
||||||
|
- `$SYS.REQ.SERVER.PING` (discover servers - will return multiple messages)
|
||||||
|
|
||||||
|
|
||||||
|
Servers like `nats-account-server` publish system account messages when a claim is updated, the nats-server listens for them, and updates its account information accordingly:
|
||||||
|
- `$SYS.ACCOUNT.<id>.CLAIMS.UPDATE`
|
||||||
|
|
||||||
|
|
||||||
|
With these few messages you can build fairly surprisingly useful monitoring tools:
|
||||||
|
|
||||||
|
- health/load of your servers
|
||||||
|
- client connects/disconnects
|
||||||
|
- account connections
|
||||||
|
- authentication errors
|
||||||
|
|
||||||
|
### Enabling System Events
|
||||||
|
|
||||||
|
To enable and access system events, you'll have to:
|
||||||
|
|
||||||
|
- Create an Operator, Account and User
|
||||||
|
- Run a NATS Account Server (or Memory Resolver)
|
||||||
|
|
||||||
|
|
||||||
|
#### Create an Operator, Account, User
|
||||||
|
Let's create an operator, system account and system account user:
|
||||||
|
|
||||||
|
```text
|
||||||
|
# Create an operator if you
|
||||||
|
> nsc add operator -n SAOP
|
||||||
|
Generated operator key - private key stored "~/.nkeys/SAOP/SAOP.nk"
|
||||||
|
Success! - added operator "SAOP"
|
||||||
|
|
||||||
|
# Add the system account
|
||||||
|
> nsc add account -n SYS
|
||||||
|
Generated account key - private key stored "~/.nkeys/SAOP/accounts/SYS/SYS.nk"
|
||||||
|
Success! - added account "SYS"
|
||||||
|
|
||||||
|
# Add a system account user
|
||||||
|
> nsc add user -n SYSU
|
||||||
|
Generated user key - private key stored "~/.nkeys/SAOP/accounts/SYS/users/SYSU.nk"
|
||||||
|
Generated user creds file "~/.nkeys/SAOP/accounts/SYS/users/SYSU.creds"
|
||||||
|
Success! - added user "SYSU" to "SYS"
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the operator JWT can be found in `~/.nsc/nats/<operator_name>/<operator.name>.jwt`.
|
||||||
|
|
||||||
|
#### Nats-Account-Server
|
||||||
|
|
||||||
|
To vend the credentials to the nats-server, we'll use a [nats-account-server](/nats_tools/nas/nas.md). Let's start a nats-account-server to serve the JWT credentials:
|
||||||
|
```text
|
||||||
|
> nats-account-server -nsc ~/.nsc/nats/SAOP
|
||||||
|
```
|
||||||
|
|
||||||
|
The server will by default vend JWT configurations on the an endpoint at: `http(s)://<server_url>/jwt/v1/accounts/`.
|
||||||
|
|
||||||
|
#### NATS Server Configuration
|
||||||
|
|
||||||
|
The server configuration will need:
|
||||||
|
- The operator JWT - (`~/.nsc/nats/<operator_name>/<operator.name>.jwt`)
|
||||||
|
- The URL where the server can resolve accounts (`http://localhost:9090/jwt/v1/accounts/`)
|
||||||
|
- The public key of the `system_account`
|
||||||
|
|
||||||
|
The only thing we don't have handy is the public key for the system account. We can get it easy enough:
|
||||||
|
```text
|
||||||
|
> nsc list accounts -W
|
||||||
|
╭─────────────────────────────────────────────────────────────────╮
|
||||||
|
│ Accounts │
|
||||||
|
├──────┬──────────────────────────────────────────────────────────┤
|
||||||
|
│ Name │ Public Key │
|
||||||
|
├──────┼──────────────────────────────────────────────────────────┤
|
||||||
|
│ SYS │ ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF │
|
||||||
|
╰──────┴──────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the server has additional resolver implementations, you need to enclose the server url like: `URL(<url>)`.
|
||||||
|
|
||||||
|
|
||||||
|
Let's create server config with the following contents and save it to `server.conf`:
|
||||||
|
```text
|
||||||
|
operator: /Users/synadia/.nsc/nats/SAOP/SAOP.jwt
|
||||||
|
system_account: ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF
|
||||||
|
resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Let's start the nats-server:
|
||||||
|
```text
|
||||||
|
> nats-server -c server.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inspecting Server Events
|
||||||
|
|
||||||
|
Let's add a subscriber for all the events published by the system account:
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nats-sub -creds ~/.nkeys/SAOP/accounts/SYS/users/SYSU.creds ">"
|
||||||
|
```
|
||||||
|
Very quickly we'll start seeing messages from the server as they are published by the NATS server. As should be expected, the messages are just JSON, so they can easily be inspected even if just using a simple `nats-sub` to read them.
|
||||||
|
|
||||||
|
To see an an account update:
|
||||||
|
```text
|
||||||
|
> nats-pub -creds ~/.nkeys/SAOP/accounts/SYS/users/SYSU.creds foo bar
|
||||||
|
```
|
||||||
|
|
||||||
|
The subscriber will print the connect and disconnect:
|
||||||
|
```text
|
||||||
|
[#35] Received on [$SYS.SERVER.ACCOUNT.ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF.CONNS]: '{
|
||||||
|
"server": {
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"id": "NBTGVY3OKDKEAJPUXRHZLKBCRH3LWCKZ6ZXTAJRS2RMYN3PMDRMUZWPR",
|
||||||
|
"ver": "2.0.0-RC5",
|
||||||
|
"seq": 32,
|
||||||
|
"time": "2019-05-03T14:53:15.455266-05:00"
|
||||||
|
},
|
||||||
|
"acc": "ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF",
|
||||||
|
"conns": 1,
|
||||||
|
"total_conns": 1
|
||||||
|
}'
|
||||||
|
[#36] Received on [$SYS.ACCOUNT.ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF.DISCONNECT]: '{
|
||||||
|
"server": {
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"id": "NBTGVY3OKDKEAJPUXRHZLKBCRH3LWCKZ6ZXTAJRS2RMYN3PMDRMUZWPR",
|
||||||
|
"ver": "2.0.0-RC5",
|
||||||
|
"seq": 33,
|
||||||
|
"time": "2019-05-03T14:53:15.455304-05:00"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"start": "2019-05-03T14:53:15.453824-05:00",
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"id": 6,
|
||||||
|
"acc": "ADWJVSUSEVC2GHL5GRATN2LOEOQOY2E6Z2VXNU3JEIK6BDGPWNIW3AXF",
|
||||||
|
"user": "UACPEXCAZEYWZK4O52MEGWGK4BH3OSGYM3P3C3F3LF2NGNZUS24IVG36",
|
||||||
|
"name": "NATS Sample Publisher",
|
||||||
|
"lang": "go",
|
||||||
|
"ver": "1.7.0",
|
||||||
|
"stop": "2019-05-03T14:53:15.45526-05:00"
|
||||||
|
},
|
||||||
|
"sent": {
|
||||||
|
"msgs": 1,
|
||||||
|
"bytes": 3
|
||||||
|
},
|
||||||
|
"received": {
|
||||||
|
"msgs": 0,
|
||||||
|
"bytes": 0
|
||||||
|
},
|
||||||
|
"reason": "Client Closed"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### `$SYS.REQ.SERVER.PING` - Discovering Servers
|
||||||
|
|
||||||
|
To discover servers in the cluster, and get a small heath summary, publish a request to `$SYS.REQ.SERVER.PING`. Note that while the example below uses `nats-req`, only the first answer for the request will be printed. You can easily modify the example to wait until no additional responses are received for a specific amount of time, thus allowing for all responses to be collected.
|
||||||
|
|
||||||
|
```text
|
||||||
|
> nats-req -creds ~/.nkeys/SAOP/accounts/SYS/users/SYSU.creds \$SYS.REQ.SERVER.PING ""
|
||||||
|
Published [$SYS.REQ.SERVER.PING] : ''
|
||||||
|
Received [_INBOX.G5mbsf0k7l7nb4eWHa7GTT.omklmvnm] : '{
|
||||||
|
"server": {
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"id": "NCZQDUX77OSSTGN2ESEOCP4X7GISMARX3H4DBGZBY34VLAI4TQEPK6P6",
|
||||||
|
"ver": "2.0.0-RC9",
|
||||||
|
"seq": 47,
|
||||||
|
"time": "2019-05-02T14:02:46.402166-05:00"
|
||||||
|
},
|
||||||
|
"statsz": {
|
||||||
|
"start": "2019-05-02T13:41:01.113179-05:00",
|
||||||
|
"mem": 12922880,
|
||||||
|
"cores": 20,
|
||||||
|
"cpu": 0,
|
||||||
|
"connections": 2,
|
||||||
|
"total_connections": 2,
|
||||||
|
"active_accounts": 1,
|
||||||
|
"subscriptions": 10,
|
||||||
|
"sent": {
|
||||||
|
"msgs": 7,
|
||||||
|
"bytes": 2761
|
||||||
|
},
|
||||||
|
"received": {
|
||||||
|
"msgs": 0,
|
||||||
|
"bytes": 0
|
||||||
|
},
|
||||||
|
"slow_consumers": 0
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### `$SYS.SERVER.<id>.STATSZ` - Requesting Server Stats Summary
|
||||||
|
|
||||||
|
If you know the server id for a particular server (such as from a response to `$SYS.REQ.SERVER.PING`), you can query the specific server for its health information:
|
||||||
|
|
||||||
|
```text
|
||||||
|
nats-req -creds ~/.nkeys/SAOP/accounts/SYS/users/SYSU.creds \$SYS.REQ.SERVER.NC7AKPQRC6CIZGWRJOTVFIGVSL7VW7WXTQCTUJFNG7HTCMCKQTGE5PUL.STATSZ ""
|
||||||
|
Published [$SYS.REQ.SERVER.NC7AKPQRC6CIZGWRJOTVFIGVSL7VW7WXTQCTUJFNG7HTCMCKQTGE5PUL.STATSZ] : ''
|
||||||
|
Received [_INBOX.DQD44ugVt0O4Ur3pWIOOD1.WQOBevoq] : '{
|
||||||
|
"server": {
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"id": "NC7AKPQRC6CIZGWRJOTVFIGVSL7VW7WXTQCTUJFNG7HTCMCKQTGE5PUL",
|
||||||
|
"ver": "2.0.0-RC5",
|
||||||
|
"seq": 25,
|
||||||
|
"time": "2019-05-03T14:34:02.066077-05:00"
|
||||||
|
},
|
||||||
|
"statsz": {
|
||||||
|
"start": "2019-05-03T14:32:19.969037-05:00",
|
||||||
|
"mem": 11874304,
|
||||||
|
"cores": 20,
|
||||||
|
"cpu": 0,
|
||||||
|
"connections": 2,
|
||||||
|
"total_connections": 4,
|
||||||
|
"active_accounts": 1,
|
||||||
|
"subscriptions": 10,
|
||||||
|
"sent": {
|
||||||
|
"msgs": 26,
|
||||||
|
"bytes": 9096
|
||||||
|
},
|
||||||
|
"received": {
|
||||||
|
"msgs": 2,
|
||||||
|
"bytes": 0
|
||||||
|
},
|
||||||
|
"slow_consumers": 0
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user