1
0
mirror of https://github.com/taigrr/nats.docs synced 2025-01-18 04:03:23 -08:00

fix folder structure from gitbook import

This commit is contained in:
ainsley
2019-10-07 14:20:40 -05:00
parent fb1b7b9a2b
commit 7681f14c27
44 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
# Securing Connections
NATS provides several forms of security, authentication, authorization and isolation. You can turn on authentication which limits access to the NATS system. Accounts allow for isolation of a subject space and groups of applications. Authorization can be used to limit individual users access to specific subjects for publish and subscribe operations. TLS can be used to encrypt all traffic between clients and the NATS system. Finally, TLS can be used to verify client identities using client certificates. By combining all of these methods you can protect access to the system and to all message flows.
The client doesn't have control over access controls, but clients do provide the configurations required to authenticate with the system, bind to an account, and to require TLS.

View File

@@ -0,0 +1,92 @@
# Authenticating with a Credentials File
The 2.0 version of NATS server introduced the idea of JWT-based authentication. Clients interact with this new scheme using a user JWT and the private key from an NKey pair. To help make connecting with a JWT easier, the client libraries support the concept of a credentials file. This file contains both the private key and the JWT and can be generated with the `nsc` tool. The contents will look like the following and should be protected because it contains a private key. This creds file is unused and only for example purposes.
```text
-----BEGIN NATS USER JWT-----
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJUVlNNTEtTWkJBN01VWDNYQUxNUVQzTjRISUw1UkZGQU9YNUtaUFhEU0oyWlAzNkVMNVJBIiwiaWF0IjoxNTU4MDQ1NTYyLCJpc3MiOiJBQlZTQk0zVTQ1REdZRVVFQ0tYUVM3QkVOSFdHN0tGUVVEUlRFSEFKQVNPUlBWV0JaNEhPSUtDSCIsIm5hbWUiOiJvbWVnYSIsInN1YiI6IlVEWEIyVk1MWFBBU0FKN1pEVEtZTlE3UU9DRldTR0I0Rk9NWVFRMjVIUVdTQUY3WlFKRUJTUVNXIiwidHlwZSI6InVzZXIiLCJuYXRzIjp7InB1YiI6e30sInN1YiI6e319fQ.6TQ2ilCDb6m2ZDiJuj_D_OePGXFyN3Ap2DEm3ipcU5AhrWrNvneJryWrpgi_yuVWKo1UoD5s8bxlmwypWVGFAA
------END NATS USER JWT------
************************* IMPORTANT *************************
NKEY Seed printed below can be used to sign and prove identity.
NKEYs are sensitive and should be treated as secrets.
-----BEGIN USER NKEY SEED-----
SUAOY5JZ2WJKVR4UO2KJ2P3SW6FZFNWEOIMAXF4WZEUNVQXXUOKGM55CYE
------END USER NKEY SEED------
*************************************************************
```
Given a creds file, a client can authenticate as a specific user belonging to a specific account:
{% tabs %}
{% tab title="Go" %}
```go
nc, err := nats.Connect("127.0.0.1", nats.UserCredentials("path_to_creds_file"))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
Options options = new Options.Builder().
server("nats://localhost:4222").
authHandler(Nats.credentials("path_to_creds_file")).
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
// credentials file contains the JWT and the secret signing key
let credsFile = path.join(confDir, 'credsfile.creds');
let nc = NATS.connect({
url: server.nats,
userCreds: credsFile
});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
async def error_cb(e):
print("Error:", e)
await nc.connect("nats://localhost:4222",
user_credentials="path_to_creds_file",
error_cb=error_cb,
)
# Do something with the connection
await nc.close()
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
// credentials file contains the JWT and the secret signing key
let credsFile = path.join(confDir, 'credsfile.creds');
let nc = await connect({
url: server.nats,
userCreds: credsFile
});
```
{% endtab %}
{% endtabs %}

View File

@@ -0,0 +1,109 @@
# Authenticating with an NKey
The 2.0 version of NATS server introduces a new challenge response authentication option. This challenge response is based on a wrapper we call NKeys which uses [Ed25519](https://ed25519.cr.yp.to/) signing. The server can use these keys in several ways for authentication. The simplest is for the server to be configured with a list of known public keys and for the clients to respond to the challenge by signing it with its private key. This challenge-response ensures security by ensuring that the client has the private key, but also protects the private key from the server which never has to actually see it.
Handling challenge response may require more than just a setting in the connection options, depending on the client library.
{% tabs %}
{% tab title="Go" %}
```go
opt, err := nats.NkeyOptionFromSeed("seed.txt")
if err != nil {
log.Fatal(err)
}
nc, err := nats.Connect("127.0.0.1", opt)
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
NKey theNKey = NKey.createUser(null); // really should load from somewhere
Options options = new Options.Builder().
server("nats://localhost:4222").
authHandler(new AuthHandler(){
public char[] getID() {
try {
return theNKey.getPublicKey();
} catch (GeneralSecurityException|IOException|NullPointerException ex) {
return null;
}
}
public byte[] sign(byte[] nonce) {
try {
return theNKey.sign(nonce);
} catch (GeneralSecurityException|IOException|NullPointerException ex) {
return null;
}
}
public char[] getJWT() {
return null;
}
}).
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
// seed should be stored in a file and treated like a secret
const seed = 'SUAEL6GG2L2HIF7DUGZJGMRUFKXELGGYFMHF76UO2AYBG3K4YLWR3FKC2Q';
let nc = NATS.connect({
url: server.nats,
nkey: 'UD6OU4D3CIOGIDZVL4ANXU3NWXOW5DCDE2YPZDBHPBXCVKHSODUA4FKI',
sigCB: function (nonce) {
const sk = nkeys.fromSeed(Buffer.from(seed));
return sk.sign(nonce);
}
});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
async def error_cb(e):
print("Error:", e)
await nc.connect("nats://localhost:4222",
nkeys_seed="./path/to/nkeys/user.nk",
error_cb=error_cb,
)
# Do something with the connection
await nc.close()
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
// seed should be stored in a file and treated like a secret
const seed = 'SUAEL6GG2L2HIF7DUGZJGMRUFKXELGGYFMHF76UO2AYBG3K4YLWR3FKC2Q';
let nc = await connect({
url: server.nats,
nkey: 'UD6OU4D3CIOGIDZVL4ANXU3NWXOW5DCDE2YPZDBHPBXCVKHSODUA4FKI',
nonceSigner: function (nonce) {
const sk = fromSeed(Buffer.from(seed));
return sk.sign(Buffer.from(nonce));
}
});
```
{% endtab %}
{% endtabs %}

View File

@@ -0,0 +1,384 @@
# Encrypting Connections with TLS
While authentication limits which clients can connect, TLS can be used to check the servers identity and optionally the clients identity and will encrypt all traffic between the two. The most secure version of TLS with NATS is to use verified client certificates. In this mode, the client can check that it trusts the certificate sent by NATS system but the individual server will also check that it trusts the certificate sent by the client. From an application's perspective connecting to a server that does not verify client certificates may appear identical. Under the covers, disabling TLS verification removes the server side check on the clients certificate. When started in TLS mode, a `nats-server` will require all clients to connect with TLS. Moreover, if configured to connect with TLS, client libraries will fail to connect to a server without TLS.
The [Java examples repository](https://github.com/nats-io/java-nats-examples/tree/master/src/main/resources) contains certificates for starting the server in TLS mode.
```bash
> nats-server -c /src/main/resources/tls.conf
or
> nats-server -c /src/main/resources/tls_verify.conf
```
## Connecting with TLS
Connecting to a server with TLS is straightforward. Most clients will automatically use TLS when connected to a NATS system using TLS. Setting up a NATS system to use TLS is primarily an exercise in setting up the certificate and trust managers. Clients may also need additional information, for example:
{% tabs %}
{% tab title="Go" %}
```go
nc, err := nats.Connect("localhost",
nats.ClientCert("resources/certs/cert.pem", "resources/certs/key.pem"),
nats.RootCAs("resources/certs/ca.pem"))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
class SSLUtils {
public static String KEYSTORE_PATH = "src/main/resources/keystore.jks";
public static String TRUSTSTORE_PATH = "src/main/resources/cacerts";
public static String STORE_PASSWORD = "password";
public static String KEY_PASSWORD = "password";
public static String ALGORITHM = "SunX509";
public static KeyStore loadKeystore(String path) throws Exception {
KeyStore store = KeyStore.getInstance("JKS");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
try {
store.load(in, STORE_PASSWORD.toCharArray());
} finally {
if (in != null) {
in.close();
}
}
return store;
}
public static KeyManager[] createTestKeyManagers() throws Exception {
KeyStore store = loadKeystore(KEYSTORE_PATH);
KeyManagerFactory factory = KeyManagerFactory.getInstance(ALGORITHM);
factory.init(store, KEY_PASSWORD.toCharArray());
return factory.getKeyManagers();
}
public static TrustManager[] createTestTrustManagers() throws Exception {
KeyStore store = loadKeystore(TRUSTSTORE_PATH);
TrustManagerFactory factory = TrustManagerFactory.getInstance(ALGORITHM);
factory.init(store);
return factory.getTrustManagers();
}
public static SSLContext createSSLContext() throws Exception {
SSLContext ctx = SSLContext.getInstance(Options.DEFAULT_SSL_PROTOCOL);
ctx.init(createTestKeyManagers(), createTestTrustManagers(), new SecureRandom());
return ctx;
}
}
public class ConnectTLS {
public static void main(String[] args) {
try {
SSLContext ctx = SSLUtils.createSSLContext();
Options options = new Options.Builder().
server("nats://localhost:4222").
sslContext(ctx). // Set the SSL context
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let caCert = fs.readFileSync(caCertPath);
let clientCert = fs.readFileSync(clientCertPath);
let clientKey = fs.readFileSync(clientKeyPath);
let nc = NATS.connect({
url: url,
tls: {
ca: [caCert],
key: [clientKey],
cert: [clientCert]
}
});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
ssl_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
ssl_ctx.load_verify_locations('ca.pem')
ssl_ctx.load_cert_chain(certfile='client-cert.pem',
keyfile='client-key.pem')
await nc.connect(io_loop=loop, tls=ssl_ctx)
await nc.connect(servers=["nats://demo.nats.io:4222"], tls=ssl_ctx)
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
EM.run do
options = {
:servers => [
'nats://localhost:4222',
],
:tls => {
:private_key_file => './spec/configs/certs/key.pem',
:cert_chain_file => './spec/configs/certs/server.pem'
}
}
NATS.connect(options) do |nc|
puts "#{Time.now.to_f} - Connected to NATS at #{nc.connected_server}"
nc.subscribe("hello") do |msg|
puts "#{Time.now.to_f} - Received: #{msg}"
end
nc.flush do
nc.publish("hello", "world")
end
EM.add_periodic_timer(0.1) do
next unless nc.connected?
nc.publish("hello", "hello")
end
# Set default callbacks
nc.on_error do |e|
puts "#{Time.now.to_f } - Error: #{e}"
end
nc.on_disconnect do |reason|
puts "#{Time.now.to_f} - Disconnected: #{reason}"
end
nc.on_reconnect do |nc|
puts "#{Time.now.to_f} - Reconnected to NATS server at #{nc.connected_server}"
end
nc.on_close do
puts "#{Time.now.to_f} - Connection to NATS closed"
EM.stop
end
end
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let caCert = readFileSync(caCertPath);
let clientCert = readFileSync(clientCertPath);
let clientKey = readFileSync(clientKeyPath);
let nc = await connect({
url: url,
tls: {
ca: [caCert],
key: [clientKey],
cert: [clientCert]
}
});
```
{% endtab %}
{% endtabs %}
## Connecting with the TLS Protocol
Some clients may support the `tls` protocol as well as a manual setting to turn on TLS. However, in that case there is likely some form of default or environmental settings to allow the TLS libraries to find certificate and trust stores.
{% tabs %}
{% tab title="Go" %}
```go
nc, err := nats.Connect("tls://localhost", nats.RootCAs("resources/certs/ca.pem")) // May need this if server is using self-signed certificate
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
class SSLUtils {
public static String KEYSTORE_PATH = "src/main/resources/keystore.jks";
public static String TRUSTSTORE_PATH = "src/main/resources/cacerts";
public static String STORE_PASSWORD = "password";
public static String KEY_PASSWORD = "password";
public static String ALGORITHM = "SunX509";
public static KeyStore loadKeystore(String path) throws Exception {
KeyStore store = KeyStore.getInstance("JKS");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));
try {
store.load(in, STORE_PASSWORD.toCharArray());
} finally {
if (in != null) {
in.close();
}
}
return store;
}
public static KeyManager[] createTestKeyManagers() throws Exception {
KeyStore store = loadKeystore(KEYSTORE_PATH);
KeyManagerFactory factory = KeyManagerFactory.getInstance(ALGORITHM);
factory.init(store, KEY_PASSWORD.toCharArray());
return factory.getKeyManagers();
}
public static TrustManager[] createTestTrustManagers() throws Exception {
KeyStore store = loadKeystore(TRUSTSTORE_PATH);
TrustManagerFactory factory = TrustManagerFactory.getInstance(ALGORITHM);
factory.init(store);
return factory.getTrustManagers();
}
public static SSLContext createSSLContext() throws Exception {
SSLContext ctx = SSLContext.getInstance(Options.DEFAULT_SSL_PROTOCOL);
ctx.init(createTestKeyManagers(), createTestTrustManagers(), new SecureRandom());
return ctx;
}
}
public class ConnectTLS {
public static void main(String[] args) {
try {
SSLContext ctx = SSLUtils.createSSLContext();
Options options = new Options.Builder().
server("nats://localhost:4222").
sslContext(ctx). // Set the SSL context
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let caCert = fs.readFileSync(caCertPath);
let clientCert = fs.readFileSync(clientCertPath);
let clientKey = fs.readFileSync(clientKeyPath);
let nc = NATS.connect({
url: url,
tls: {
ca: [caCert],
key: [clientKey],
cert: [clientCert]
}
});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
ssl_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
ssl_ctx.load_verify_locations('ca.pem')
ssl_ctx.load_cert_chain(certfile='client-cert.pem',
keyfile='client-key.pem')
await nc.connect(io_loop=loop, tls=ssl_ctx)
await nc.connect(servers=["nats://demo.nats.io:4222"], tls=ssl_ctx)
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
EM.run do
options = {
:servers => [
'nats://localhost:4222',
],
:tls => {
:private_key_file => './spec/configs/certs/key.pem',
:cert_chain_file => './spec/configs/certs/server.pem'
}
}
NATS.connect(options) do |nc|
puts "#{Time.now.to_f} - Connected to NATS at #{nc.connected_server}"
nc.subscribe("hello") do |msg|
puts "#{Time.now.to_f} - Received: #{msg}"
end
nc.flush do
nc.publish("hello", "world")
end
EM.add_periodic_timer(0.1) do
next unless nc.connected?
nc.publish("hello", "hello")
end
# Set default callbacks
nc.on_error do |e|
puts "#{Time.now.to_f } - Error: #{e}"
end
nc.on_disconnect do |reason|
puts "#{Time.now.to_f} - Disconnected: #{reason}"
end
nc.on_reconnect do |nc|
puts "#{Time.now.to_f} - Reconnected to NATS server at #{nc.connected_server}"
end
nc.on_close do
puts "#{Time.now.to_f} - Connection to NATS closed"
EM.stop
end
end
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let caCert = readFileSync(caCertPath);
let clientCert = readFileSync(clientCertPath);
let clientKey = readFileSync(clientKeyPath);
let nc = await connect({
url: url,
tls: {
ca: [caCert],
key: [clientKey],
cert: [clientCert]
}
});
```
{% endtab %}
{% endtabs %}

View File

@@ -0,0 +1,138 @@
# Authenticating with a Token
Tokens are basically random strings, much like a password, and can provide a simple authentication mechanism in some situations. However, tokens are only as safe as they are secret so other authentication schemes can provide more security in large installations.
For this example, start the server using:
```bash
> nats-server --auth mytoken
```
The code uses localhost:4222 so that you can start the server on your machine to try them out.
## Connecting with a Token
{% tabs %}
{% tab title="Go" %}
```go
// Set a token
nc, err := nats.Connect("127.0.0.1", nats.Name("API Token Example"), nats.Token("mytoken"))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
Options options = new Options.Builder().
server("nats://localhost:4222").
token("mytoken"). // Set a token
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let nc = NATS.connect({url: `nats://127.0.0.1:${port}`, token: "mytoken!"});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
await nc.connect(servers=["nats://mytoken@demo.nats.io:4222"])
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
NATS.start(token: "deadbeef") do |nc|
puts "Connected using token"
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let nc = await connect({url: server.nats, token: "mytoken"});
```
{% endtab %}
{% endtabs %}
## Connecting with a Token in the URL
Some client libraries will allow you to pass the token as part of the server URL using the form:
> nats://_token_@server:port
Again, once you construct this URL you can connect as if this was a normal URL.
{% tabs %}
{% tab title="Go" %}
```go
// Token in URL
nc, err := nats.Connect("mytoken@localhost")
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
Connection nc = Nats.connect("nats://mytoken@localhost:4222");//Token in URL
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let url = `nats://mytoken!@127.0.0.1:${port}`;
let nc = NATS.connect({url: url});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
await nc.connect(servers=["nats://mytoken@demo.nats.io:4222"])
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
NATS.start("deadbeef@127.0.0.1:4222") do |nc|
puts "Connected using token!"
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let url = `nats://:mytoken!@127.0.0.1:${port}`;
let nc = await connect({url: url});
```
{% endtab %}
{% endtabs %}

View File

@@ -0,0 +1,176 @@
# Authenticating with a User and Password
For this example, start the server using:
```bash
> nats-server --user myname --pass password
```
You can encrypt passwords to pass to `nats-server` using a simple tool provided by the server:
```bash
> go run mkpasswd.go -p
> password: password
> bcrypt hash: $2a$11$1oJy/wZYNTxr9jNwMNwS3eUGhBpHT3On8CL9o7ey89mpgo88VG6ba
```
and use the hashed password in the server config. The client still uses the plain text version.
The code uses localhost:4222 so that you can start the server on your machine to try them out.
## Connecting with a User/Password
When logging in with a password `nats-server` will take either a plain text password or an encrypted password.
{% tabs %}
{% tab title="Go" %}
```go
// Set a user and plain text password
nc, err := nats.Connect("127.0.0.1", nats.UserInfo("myname", "password"))
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
Options options = new Options.Builder().
server("nats://localhost:4222").
userInfo("myname","password"). // Set a user and plain text password
build();
Connection nc = Nats.connect(options);
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let nc = NATS.connect({url: server.nats, user: "myname", pass: "password"});
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
await nc.connect(servers=["nats://myname:password@demo.nats.io:4222"])
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
require 'nats/client'
NATS.start(servers:["nats://myname:password@127.0.0.1:4222"], name: "my-connection") do |nc|
nc.on_error do |e|
puts "Error: #{e}"
end
nc.on_reconnect do
puts "Got reconnected to #{nc.connected_server}"
end
nc.on_disconnect do |reason|
puts "Got disconnected! #{reason}"
end
nc.close
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let nc = await connect({url: server.nats, user: "myname", pass: "password"});
```
{% endtab %}
{% endtabs %}
## Connecting with a User/Password in the URL
Most clients make it easy to pass the user name and password by accepting them in the URL for the server. This standard format is:
> nats://_user_:_password_@server:port
Using this format, you can connect to a server using authentication as easily as you connected with a URL:
{% tabs %}
{% tab title="Go" %}
```go
// Set a user and plain text password
nc, err := nats.Connect("myname:password@127.0.0.1")
if err != nil {
log.Fatal(err)
}
defer nc.Close()
// Do something with the connection
```
{% endtab %}
{% tab title="Java" %}
```java
Connection nc = Nats.connect("nats://myname:password@localhost:4222");
// Do something with the connection
nc.close();
```
{% endtab %}
{% tab title="JavaScript" %}
```javascript
let url = `nats://myname:password@127.0.0.1:${port}`;
let nc = NATS.connect(url);
```
{% endtab %}
{% tab title="Python" %}
```python
nc = NATS()
await nc.connect(servers=["nats://myname:password@demo.nats.io:4222"])
# Do something with the connection.
```
{% endtab %}
{% tab title="Ruby" %}
```ruby
require 'nats/client'
NATS.start(servers:["nats://myname:password@127.0.0.1:4222"], name: "my-connection") do |nc|
nc.on_error do |e|
puts "Error: #{e}"
end
nc.on_reconnect do
puts "Got reconnected to #{nc.connected_server}"
end
nc.on_disconnect do |reason|
puts "Got disconnected! #{reason}"
end
nc.close
end
```
{% endtab %}
{% tab title="TypeScript" %}
```typescript
let url = `nats://myname:password@127.0.0.1:${port}`;
let nc = await connect({url: url});
```
{% endtab %}
{% endtabs %}