mirror of
https://github.com/taigrr/nats.docs
synced 2025-01-18 04:03:23 -08:00
GitBook: [master] 61 pages modified
This commit is contained in:
committed by
gitbook-bot
parent
ed6c1ba06d
commit
de933e912d
@@ -3,5 +3,6 @@
|
||||
The NATS server provides several forms of security:
|
||||
|
||||
* Connections can be [_encrypted_ with TLS](tls.md)
|
||||
* Client connections can require [_authentication_](auth_intro/README.md)
|
||||
* Client connections can require [_authentication_](auth_intro/)
|
||||
* Clients can require [_authorization_](authorization.md) for subjects they publish or subscribe to
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
# Multi Tenancy
|
||||
# Multi Tenancy using Accounts
|
||||
|
||||
In modern microservice architecture it is common to share infrastructure - such as NATS - between services.
|
||||
[Accounts](#Accounts) are securely isolated communication contexts that allow multi-tenancy in a NATS deployment.
|
||||
They allow users to bifurcate technology from business driven use cases, where data silos are created by design, not software limitations.
|
||||
Furthermore, they facilitate the [controlled exchange](#Exporting-and-Importing) of information between those data silos/Tenants/Accounts.
|
||||
In modern microservice architecture it is common to share infrastructure - such as NATS - between services. [Accounts](accounts.md#Accounts) are securely isolated communication contexts that allow multi-tenancy in a NATS deployment. They allow users to bifurcate technology from business driven use cases, where data silos are created by design, not software limitations. Furthermore, they facilitate the [controlled exchange](accounts.md#Exporting-and-Importing) of information between those data silos/Tenants/Accounts.
|
||||
|
||||
## Accounts
|
||||
|
||||
_Accounts_ expand on the authorization foundation. With traditional authorization, all clients can publish and subscribe to anything unless explicitly configured otherwise. To protect clients and information, you have to carve the subject space and permission clients carefully.
|
||||
|
||||
_Accounts_ allow the grouping of clients, _isolating_ them from clients in 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. [System Events](../sys_accounts/README.md) are an example of this isolation at work.
|
||||
_Accounts_ allow the grouping of clients, _isolating_ them from clients in 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. [System Events](../sys_accounts/) are an example of this isolation at work.
|
||||
|
||||
Accounts configuration is done in `accounts` map. The contents of an account entry includes:
|
||||
|
||||
| Property | Description |
|
||||
| :--- | :--- |
|
||||
| `users` | a list of [user configuration maps](auth_intro/README.md#user-configuration-map) |
|
||||
| `exports` | a list of [export maps](#Export-Configuration-Map) |
|
||||
| `imports` | a list of [import maps](#Import-Configuration-Map) |
|
||||
| `users` | a list of [user configuration maps](auth_intro/#user-configuration-map) |
|
||||
| `exports` | a list of [export maps](accounts.md#Export-Configuration-Map) |
|
||||
| `imports` | a list of [import maps](accounts.md#Import-Configuration-Map) |
|
||||
|
||||
The `accounts` list is a map, where the keys on the map are an account name.
|
||||
|
||||
@@ -40,7 +37,7 @@ accounts: {
|
||||
>
|
||||
> These two accounts are isolated from each other. Messages published by users in `A` are not visible to users in `B`.
|
||||
>
|
||||
> The user configuration map is the same as any other NATS [user configuration map](auth_intro/README.md#user-configuration-map) . You can use:
|
||||
> The user configuration map is the same as any other NATS [user configuration map](auth_intro/#user-configuration-map) . You can use:
|
||||
>
|
||||
> * username/password
|
||||
> * nkeys
|
||||
@@ -55,8 +52,7 @@ Messaging exchange between different accounts is enabled by _exporting_ streams
|
||||
* **Streams** are messages your application publishes. Importing applications won't be able to make requests from your applications but will be able to consume messages you generate.
|
||||
* **Services** are messages your application can consume and act on, enabling other accounts to make requests that are fulfilled by your account.
|
||||
|
||||
The `exports` configuration list enable you to define the services and streams that others can import. Exported services and streams are expressed as an [Export configuration map](#export-configuration-map).
|
||||
The `imports` configuration lists the services and streams that an Account imports. Imported services and streams are expressed as an [Import configuration map](#import-configuration-map).
|
||||
The `exports` configuration list enable you to define the services and streams that others can import. Exported services and streams are expressed as an [Export configuration map](accounts.md#export-configuration-map). The `imports` configuration lists the services and streams that an Account imports. Imported services and streams are expressed as an [Import configuration map](accounts.md#import-configuration-map).
|
||||
|
||||
### Export Configuration Map
|
||||
|
||||
@@ -100,15 +96,12 @@ An import enables an account to consume streams published by another account or
|
||||
|
||||
| Property | Description |
|
||||
| :--- | :--- |
|
||||
| `stream` | Stream import [source configuration](#source-configuration-map). \(exclusive of `service`\) |
|
||||
| `service` | Service import [source configuration](#source-configuration-map) \(exclusive of `stream`\) |
|
||||
| `prefix` | A local subject prefix mapping for the imported stream. \(applicable to `stream`\)|
|
||||
| `to` | A local subject mapping for imported service. \(applicable to `service`\)|
|
||||
| `stream` | Stream import [source configuration](accounts.md#source-configuration-map). \(exclusive of `service`\) |
|
||||
| `service` | Service import [source configuration](accounts.md#source-configuration-map) \(exclusive of `stream`\) |
|
||||
| `prefix` | A local subject prefix mapping for the imported stream. \(applicable to `stream`\) |
|
||||
| `to` | A local subject mapping for imported service. \(applicable to `service`\) |
|
||||
|
||||
The `prefix` and `to` options are optional and allow you to remap the subject that is used locally to receive stream messages from or publish service requests to.
|
||||
This way the importing account does not depend on naming conventions picked by another.
|
||||
Currently, a service import can not make use of wildcards, which is why the import subject can be rewritten.
|
||||
A stream import may make use of wildcards. To retain information contained in the subject, it can thus only be prefixed with `prefix`...
|
||||
The `prefix` and `to` options are optional and allow you to remap the subject that is used locally to receive stream messages from or publish service requests to. This way the importing account does not depend on naming conventions picked by another. Currently, a service import can not make use of wildcards, which is why the import subject can be rewritten. A stream import may make use of wildcards. To retain information contained in the subject, it can thus only be prefixed with `prefix`...
|
||||
|
||||
#### Source Configuration Map
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ The NATS server provides various ways of authenticating clients:
|
||||
* [Username/Password credentials](username_password.md)
|
||||
* [TLS Certificate](tls_mutual_auth.md)
|
||||
* [NKEY with Challenge](nkey_auth.md)
|
||||
* [Decentralized JWT Authenticatin/Authorization](../jwt/README.md)
|
||||
* [Decentralized JWT Authenticatin/Authorization](../jwt/)
|
||||
|
||||
Authentication deals with allowing a NATS client to connect to the server. Except for JWT authentication, authentication and authorization are configured in the `authorization` section of the configuration.
|
||||
|
||||
@@ -19,7 +19,7 @@ The `authorization` block provides _authentication_ configuration as well as _au
|
||||
| [`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](#user-configuration-map) maps |
|
||||
| `users` | A list of [user configuration](./#user-configuration-map) maps |
|
||||
| [`timeout`](auth_timeout.md) | Maximum number of seconds to wait for client authentication |
|
||||
|
||||
For multiple username and password credentials, specify a `users` list.
|
||||
|
||||
@@ -58,3 +58,4 @@ const nc = NATS.connect({
|
||||
```
|
||||
|
||||
The client provides a function that it uses to parse the seed \(the private key\) and sign the connection challenge.
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ Or via the command line:
|
||||
> ./nats-server --tlsverify --tlscert=server-cert.pem --tlskey=server-key.pem --tlscacert=rootCA.pem
|
||||
```
|
||||
|
||||
This option verifies the client's certificate is signed by the CA specified in the `ca_file` option.
|
||||
When `ca_file` is not present it will default to CAs in the system trust store.
|
||||
It also makes sure that the client provides a certificate with the extended key usage `TLS Web Client Authentication`.
|
||||
This option verifies the client's certificate is signed by the CA specified in the `ca_file` option. When `ca_file` is not present it will default to CAs in the system trust store. It also makes sure that the client provides a certificate with the extended key usage `TLS Web Client Authentication`.
|
||||
|
||||
## Mapping Client Certificates To A User
|
||||
|
||||
@@ -50,9 +48,9 @@ tls {
|
||||
|
||||
> Note that `verify` was changed to `verify_and_map`.
|
||||
|
||||
When present, the server will check if a Subject Alternative Name \(SAN\) maps to a user. It will search all email addresses first, then all DNS names. If no user could be found, it will try the certificate subject.
|
||||
When present, the server will check if a Subject Alternative Name \(SAN\) maps to a user. It will search all email addresses first, then all DNS names. If no user could be found, it will try the certificate subject.
|
||||
|
||||
> Note: This mechanism will pick the user it finds first. There is no configuration to restrict this.
|
||||
> Note: This mechanism will pick the user it finds first. There is no configuration to restrict this.
|
||||
|
||||
```text
|
||||
$ openssl x509 -noout -text -in client-cert.pem
|
||||
@@ -87,8 +85,7 @@ Certificate:
|
||||
...
|
||||
```
|
||||
|
||||
> Note that for this example to work you will have to modify the user to match what is in your certificates subject.
|
||||
> In doing so, watch out for the order of attributes!
|
||||
> Note that for this example to work you will have to modify the user to match what is in your certificates subject. In doing so, watch out for the order of attributes!
|
||||
|
||||
The configuration to authorize this user would be as follows:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The NATS server supports authorization using subject-level permissions on a per-user basis. Permission-based authorization is available with multi-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 configuration, you can specify a `permission` object which explicitly allows or denies subjects. The specified subjects can specify wildcards as well. Permissions can make use of [variables](../README.md#variables).
|
||||
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 configuration, you can specify a `permission` object which explicitly allows or denies subjects. The specified subjects can specify wildcards as well. Permissions can make use of [variables](../#variables).
|
||||
|
||||
A special field inside the authorization map is `default_permissions`. When present, it contains permissions that apply to users that do not have permissions associated with them.
|
||||
|
||||
@@ -12,9 +12,9 @@ The `permissions` map specify subjects that can be subscribed to or published by
|
||||
|
||||
| Property | Description |
|
||||
| :--- | :--- |
|
||||
| `publish` | subject, list of subjects, or [permission map](#permission-map) the client can publish |
|
||||
| `subscribe` | subject, list of subjects, or [permission map](#permission-map) the client can subscribe to. In this context it is possible to provide an optional queue name: `<subject> <queue>` to express queue group permissions. These permissions can also use wildcards such as `v2.*` or `>`.|
|
||||
| `allow_responses` | boolean or [responses map](#allow-responses-map), default is `false` |
|
||||
| `publish` | subject, list of subjects, or [permission map](authorization.md#permission-map) the client can publish |
|
||||
| `subscribe` | subject, list of subjects, or [permission map](authorization.md#permission-map) the client can subscribe to. In this context it is possible to provide an optional queue name: `<subject> <queue>` to express queue group permissions. These permissions can also use wildcards such as `v2.*` or `>`. |
|
||||
| `allow_responses` | boolean or [responses map](authorization.md#allow-responses-map), default is `false` |
|
||||
|
||||
## Permission Map
|
||||
|
||||
@@ -25,17 +25,16 @@ The `permission` map provides additional properties for configuring a `permissio
|
||||
| `allow` | List of subject names that are allowed to the client |
|
||||
| `deny` | List of subjects that are denied to the client |
|
||||
|
||||
**Important Note** It is important to not break request/reply patterns. In some cases \(as shown [below](#variables)\) you need to add rules for the `_INBOX.>` pattern. If an unauthorized client publishes or attempts to subscribe to a subject that has not been _allow listed_, the action fails and is logged at the server, and an error message is returned to the client. The [allow responses](#allow-responses-map) option can simplify this.
|
||||
**Important Note** It is important to not break request/reply patterns. In some cases \(as shown [below](authorization.md#variables)\) you need to add rules for the `_INBOX.>` pattern. If an unauthorized client publishes or attempts to subscribe to a subject that has not been _allow listed_, the action fails and is logged at the server, and an error message is returned to the client. The [allow responses](authorization.md#allow-responses-map) option can simplify this.
|
||||
|
||||
## Allow Responses Map
|
||||
|
||||
The `allow_responses` option dynamically allows publishing to reply subjects and works well for service responders.
|
||||
When set to `true`, only one response is allowed, meaning the permission to publish to the reply subject defaults to only once. The `allow_responses` map allows you to configure a maximum number of responses and how long the permission is valid.
|
||||
The `allow_responses` option dynamically allows publishing to reply subjects and works well for service responders. When set to `true`, only one response is allowed, meaning the permission to publish to the reply subject defaults to only once. The `allow_responses` map allows you to configure a maximum number of responses and how long the permission is valid.
|
||||
|
||||
| Property | Description |
|
||||
| :--- | :--- |
|
||||
| `max` | The maximum number of response messages that can be published. |
|
||||
| `expires` | The amount of time the permission is valid. Values such as `1s`, `1m`, `1h` (1 second, minute, hour) etc can be specified. Default doesn't have a time limit. |
|
||||
| `expires` | The amount of time the permission is valid. Values such as `1s`, `1m`, `1h` \(1 second, minute, hour\) etc can be specified. Default doesn't have a time limit. |
|
||||
|
||||
When `allow_responses` is set to `true`, it defaults to the equivalent of `{ max: 1 }` and no time limit.
|
||||
|
||||
@@ -74,7 +73,7 @@ authorization {
|
||||
}
|
||||
```
|
||||
|
||||
> `default_permissions` is a special entry. If defined, it applies to all users that don't have specific permissions set.
|
||||
> `default_permissions` is a special entry. If defined, it applies to all users that don't have specific permissions set.
|
||||
|
||||
* _admin_ has `ADMIN` permissions and can publish/subscribe on any subject. We use the wildcard `>` to match any subject.
|
||||
* _client_ is a `REQUESTOR` and can publish requests on subjects `req.a` or `req.b`, and subscribe to anything that is a response \(`_INBOX.>`\).
|
||||
@@ -114,17 +113,17 @@ authorization: {
|
||||
}
|
||||
```
|
||||
|
||||
### allow_responses
|
||||
### allow\_responses
|
||||
|
||||
Here's an example with `allow_responses`:
|
||||
|
||||
```text
|
||||
authorization: {
|
||||
users: [
|
||||
{ user: a, password: a },
|
||||
{ user: b, password: b, permissions: {subscribe: "q", allow_responses: true } },
|
||||
{ user: c, password: c, permissions: {subscribe: "q", allow_responses: { max: 5, expires: "1m" } } }
|
||||
]
|
||||
users: [
|
||||
{ user: a, password: a },
|
||||
{ user: b, password: b, permissions: {subscribe: "q", allow_responses: true } },
|
||||
{ user: c, password: c, permissions: {subscribe: "q", allow_responses: { max: 5, expires: "1m" } } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -154,3 +153,4 @@ users = [
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# Decentralized JWT Authentication and Authorization
|
||||
# Decentralized JWT Authentication/Authorization
|
||||
|
||||
With other authentication mechanisms, configuration for identifying a user and [Account](../accounts.md), is in the server configuration file. JWT authentication leverages [JSON Web Tokens \(JWT\)](https://jwt.io/) to describe the various entities supported. When a client connects, servers verify the authenticity of the request using [NKeys](../auth_intro/nkey_auth.md), download account information and validate a trust chain. Users are not directly tracked by the server, but rather verified as and belonging to an [Account](../accounts.md). This enables the management of users, without requiring server configuration updates.
|
||||
|
||||
Effectively, JWTs improve accounts and 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. These chores are eliminated. User creation can can even be performed by different entities altogether.
|
||||
Effectively, JWTs improve accounts and 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. These chores are eliminated. User creation can can even be performed by different entities altogether.
|
||||
|
||||
> Note: This scheme improves [accounts](../accounts.md). Functionalities like [isolation](../accounts.md) or defining [exports/imports](../accounts.md#exporting-and-importing) between accounts remain!
|
||||
> It moves configuration of accounts, exports/imports or users and their permissions away from the server into several trusted [JSON Web Token \(JWT\)](https://jwt.io/) that are managed separately, therefore removing the need to configure these entities in each and every server.
|
||||
> It furthermore adds functionalities like expiration and revocation fore decentralized account management
|
||||
> Note: This scheme improves [accounts](../accounts.md). Functionalities like [isolation](../accounts.md) or defining [exports/imports](../accounts.md#exporting-and-importing) between accounts remain! It moves configuration of accounts, exports/imports or users and their permissions away from the server into several trusted [JSON Web Token \(JWT\)](https://jwt.io/) that are managed separately, therefore removing the need to configure these entities in each and every server. It furthermore adds functionalities like expiration and revocation fore decentralized account management
|
||||
|
||||
## JSON Web Tokens
|
||||
|
||||
@@ -56,23 +54,13 @@ Lastly, all NATS JWTs \(Operators, Accounts, Users and others\) are expected to
|
||||
|
||||
## Decentralized Authentication and Authorization - Configuration
|
||||
|
||||
Configuration is broken up into separate steps.
|
||||
Depending on organizational needs these are performed by the same or different entities.
|
||||
Configuration is broken up into separate steps. Depending on organizational needs these are performed by the same or different entities.
|
||||
|
||||
JWT configuration is done using the [`nsc` tool](../../../../nats-tools/nsc/README.md).
|
||||
It can be set up to issue [NKeys](../auth_intro/nkey_auth.md) and corresponding JWTs for all [nkey roles](#nkey-roles): Operator/Account/User \([Example usage](../../../../nats-tools/nsc/nsc.md#creating-an-operator-account-and-user)\).
|
||||
Despite Account and User creation not happening in server configuration, this model is a centralized authentication and authorization setup.
|
||||
JWT configuration is done using the [`nsc` tool](../../../../nats-tools/nsc/). It can be set up to issue [NKeys](../auth_intro/nkey_auth.md) and corresponding JWTs for all [nkey roles](./#nkey-roles): Operator/Account/User \([Example usage](../../../../nats-tools/nsc/nsc.md#creating-an-operator-account-and-user)\). Despite Account and User creation not happening in server configuration, this model is a centralized authentication and authorization setup.
|
||||
|
||||
Provided institutional trust, it is also possible to use nsc to import account or user public [NKeys](../auth_intro/nkey_auth.md) and issue corresponding JWTs.
|
||||
This way an operator can issue account JWTs and a separate entity can issue JWTs for user associated with it's account.
|
||||
Neither entity has to be aware of the other's private Nkey.
|
||||
This not only allows users to be configured some place other than servers, but also by different organizations altogether.
|
||||
Say administrators of a NATS installation controlling operators, issuing account JWTs to individual prod/dev teams managing their own user.
|
||||
This is a fully decentralized authorization setup!
|
||||
Provided institutional trust, it is also possible to use nsc to import account or user public [NKeys](../auth_intro/nkey_auth.md) and issue corresponding JWTs. This way an operator can issue account JWTs and a separate entity can issue JWTs for user associated with it's account. Neither entity has to be aware of the other's private Nkey. This not only allows users to be configured some place other than servers, but also by different organizations altogether. Say administrators of a NATS installation controlling operators, issuing account JWTs to individual prod/dev teams managing their own user. This is a fully decentralized authorization setup!
|
||||
|
||||
With an Operator JWT in place, the server needs to be configured to trust it by specifying `operator`.
|
||||
Furthermore the server needs a way to obtain account JWTs.
|
||||
This done by either defaulting to the resolver specified in the operator jwt or by manually specifying the [resolver](resolver.md).
|
||||
Depending on your configuration an [account server](../../../../nats-tools/nsc/nsc.md#account-server-configuration) needs to be in place
|
||||
With an Operator JWT in place, the server needs to be configured to trust it by specifying `operator`. Furthermore the server needs a way to obtain account JWTs. This done by either defaulting to the resolver specified in the operator jwt or by manually specifying the [resolver](resolver.md). Depending on your configuration an [account server](../../../../nats-tools/nsc/nsc.md#account-server-configuration) needs to be in place
|
||||
|
||||
> It is possible to [mix](jwt_nkey_auth.md) JWT and [NKEY](../auth_intro/nkey_auth.md)/[Account](../accounts.md) based Authentication/Authorization.
|
||||
|
||||
> It is possible to [mix](jwt_nkey_auth.md) JWT and [NKEY](../auth_intro/nkey_auth.md)/[Account](../accounts.md) based Authentication/Authorization.
|
||||
@@ -1,16 +1,12 @@
|
||||
# Mixing NKEYS and Decentralized JWT Authentication/Authorization setup
|
||||
# Mixed Authentication/Authorization Setup
|
||||
|
||||
Mixing both [nkeys](../auth_intro/nkey_auth.md) static config and [decentralized JWT Authenticatin/Authorization](README.md)
|
||||
is possible but needs some preparation in order to be able to do it.
|
||||
Mixing both [nkeys](../auth_intro/nkey_auth.md) static config and [decentralized JWT Authenticatin/Authorization](./) is possible but needs some preparation in order to be able to do it.
|
||||
|
||||
The way this can be done is by **first** preparing a basic trusted operator setup
|
||||
that could be used in the future, and then base from that configuration to create the
|
||||
NKEYS static config using the same shared public nkeys for the accounts and then use
|
||||
clustering routes to bridge the two different auth setups during the transition.
|
||||
The way this can be done is by **first** preparing a basic trusted operator setup that could be used in the future, and then base from that configuration to create the NKEYS static config using the same shared public nkeys for the accounts and then use clustering routes to bridge the two different auth setups during the transition.
|
||||
|
||||
For example, creating the following initial setup using [NSC](../../../../nats-tools/nsc/README.md):
|
||||
For example, creating the following initial setup using [NSC](../../../../nats-tools/nsc/):
|
||||
|
||||
```sh
|
||||
```bash
|
||||
nsc add account --name SYS
|
||||
nsc add user --name sys
|
||||
nsc add account --name A
|
||||
@@ -21,7 +17,7 @@ For example, creating the following initial setup using [NSC](../../../../nats-t
|
||||
|
||||
This will then generate something like the following:
|
||||
|
||||
```sh
|
||||
```bash
|
||||
nsc list accounts
|
||||
╭─────────────────────────────────────────────────────────────────╮
|
||||
│ Accounts │
|
||||
@@ -43,15 +39,14 @@ This will then generate something like the following:
|
||||
╰──────┴──────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
We could use this configuration as the initial starting configuration for an nkeys config now,
|
||||
where all the NKEYS users public nkeys are explicitly listed (centralized auth model).
|
||||
We could use this configuration as the initial starting configuration for an nkeys config now, where all the NKEYS users public nkeys are explicitly listed \(centralized auth model\).
|
||||
|
||||
```hcl
|
||||
```text
|
||||
port = 4222
|
||||
|
||||
cluster {
|
||||
port = 6222
|
||||
|
||||
|
||||
# We will bridge two different servers with different auth models via routes
|
||||
# routes [ nats://127.0.0.1:6223 ]
|
||||
}
|
||||
@@ -77,16 +72,15 @@ accounts {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
By using `nsc` it is possible to create a mem based resolver for the trusted operator setup:
|
||||
|
||||
```
|
||||
```text
|
||||
nsc generate config --mem-resolver --sys-account SYS
|
||||
```
|
||||
|
||||
An example configuration from the second node with the trusted operator setup could then be:
|
||||
|
||||
```hcl
|
||||
```text
|
||||
port = 4223
|
||||
|
||||
cluster {
|
||||
@@ -116,11 +110,11 @@ resolver_preload = {
|
||||
}
|
||||
```
|
||||
|
||||
Even though they have different authorization mechanisms, these two servers are able to route account messages because they share the same NKEY.
|
||||
Even though they have different authorization mechanisms, these two servers are able to route account messages because they share the same NKEY.
|
||||
|
||||
We have created at least one user, in this case with creds:
|
||||
|
||||
```conf
|
||||
```text
|
||||
-----BEGIN NATS USER JWT-----
|
||||
eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5In0.eyJqdGkiOiJNRkM3V1E1N0hKUE9aWUVOTEhVRTZTWFVQTDVKTURWSkxIQzJRTkpYNUVJS0RGR1U1REhRIiwiaWF0IjoxNTc0Mzc1OTE2LCJpc3MiOiJBREZCMkpYWVRYT0pFTDZMTkFYRFJFVUdSWDM1Qk9MWkkzQjRQRkZBQzdJUlBSM09BNFFOS0JOMiIsIm5hbWUiOiJ0ZXN0Iiwic3ViIjoiVUFQT0syUDdFTjNVRkJMN1NCSlBRSzNNM0pNTEFMWVJZS1g1WFdTVk1WWUs2M1pNQkhUT0hWSlIiLCJ0eXBlIjoidXNlciIsIm5hdHMiOnsicHViIjp7ImFsbG93IjpbIl9JTkJPWC5cdTAwM2UiLCJfUl8iLCJfUl8uXHUwMDNlIiwidGVzdCIsInRlc3QuXHUwMDNlIl19LCJzdWIiOnsiYWxsb3ciOlsiX0lOQk9YLlx1MDAzZSIsIl9SXyIsIl9SXy5cdTAwM2UiLCJsYXRlbmN5Lm9uLnRlc3QiLCJ0ZXN0IiwidGVzdC5cdTAwM2UiXX19fQ.MSU2aUIBK1iUsg7h52lLrfEfTwVMF_wB3HDq75ECskxSyyDDMtk9_3957UtQF-3yoGCIhKOkWjzX8C-WXnLADw
|
||||
------END NATS USER JWT------
|
||||
@@ -133,7 +127,7 @@ SUANVBWRHHFMGHNIT6UJHPN2TGVBVIILE7VPVNEQ7DGCJ26ZD2V3KAHT4M
|
||||
*************************************************************
|
||||
```
|
||||
|
||||
And this same user is able to connect to either one of the servers (bound to 4222 and 4223 respectively):
|
||||
And this same user is able to connect to either one of the servers \(bound to 4222 and 4223 respectively\):
|
||||
|
||||
Subscriber Service:
|
||||
|
||||
@@ -141,34 +135,34 @@ Subscriber Service:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
opts := make([]nats.Option, 0)
|
||||
opts := make([]nats.Option, 0)
|
||||
|
||||
// Extract public nkey from seed
|
||||
//
|
||||
// Public: UAPOK2P7EN3UFBL7SBJPQK3M3JMLALYRYKX5XWSVMVYK63ZMBHTOHVJR
|
||||
// Private: SUANVBWRHHFMGHNIT6UJHPN2TGVBVIILE7VPVNEQ7DGCJ26ZD2V3KAHT4M
|
||||
//
|
||||
nkey, err := nats.NkeyOptionFromSeed("path/to/seed.nkey")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
opts = append(opts, nkey)
|
||||
nc, err := nats.Connect("127.0.0.1:4222", opts...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
nc.Subscribe("test", func(m *nats.Msg){
|
||||
log.Printf("[Received] %q, replying... \n", string(m.Data))
|
||||
m.Respond([]byte("pong from nkeys based server"))
|
||||
})
|
||||
// Extract public nkey from seed
|
||||
//
|
||||
// Public: UAPOK2P7EN3UFBL7SBJPQK3M3JMLALYRYKX5XWSVMVYK63ZMBHTOHVJR
|
||||
// Private: SUANVBWRHHFMGHNIT6UJHPN2TGVBVIILE7VPVNEQ7DGCJ26ZD2V3KAHT4M
|
||||
//
|
||||
nkey, err := nats.NkeyOptionFromSeed("path/to/seed.nkey")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
opts = append(opts, nkey)
|
||||
nc, err := nats.Connect("127.0.0.1:4222", opts...)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
nc.Subscribe("test", func(m *nats.Msg){
|
||||
log.Printf("[Received] %q, replying... \n", string(m.Data))
|
||||
m.Respond([]byte("pong from nkeys based server"))
|
||||
})
|
||||
|
||||
select {}
|
||||
select {}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -178,25 +172,26 @@ Requestor:
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
nc, err := nats.Connect("127.0.0.1:4223", nats.UserCredentials("path/to/user.creds"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
nc, err := nats.Connect("127.0.0.1:4223", nats.UserCredentials("path/to/user.creds"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for range time.NewTicker(1 * time.Second).C {
|
||||
resp, err := nc.Request("test", []byte("test"), 1*time.Second)
|
||||
if err != nil {
|
||||
log.Println("[Error]", err)
|
||||
continue
|
||||
}
|
||||
log.Println("[Received]", string(resp.Data))
|
||||
}
|
||||
for range time.NewTicker(1 * time.Second).C {
|
||||
resp, err := nc.Request("test", []byte("test"), 1*time.Second)
|
||||
if err != nil {
|
||||
log.Println("[Error]", err)
|
||||
continue
|
||||
}
|
||||
log.Println("[Received]", string(resp.Data))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Memory Resolver
|
||||
# Memory Resolver Tutorial
|
||||
|
||||
The `MEMORY` resolver is a server built-in resolver for account JWTs. If there are a small number of accounts, or they do not change too often this can be a simpler configuration that does not require an external account resolver. Server configuration reload is supported, meaning the preloads can be updated in the server configuration and reloaded without a server restart.
|
||||
|
||||
@@ -60,7 +60,7 @@ Success!! - generated "/tmp/server.conf"
|
||||
generated "/tmp/memory.jwt"
|
||||
```
|
||||
|
||||
If you require additional settings, you may want to consider using [`include`](../../nats-server/configuration/#include-directive) in your main configuration, to reference the generated files. Otherwise, you can start a server and reference the generated configuration:
|
||||
If you require additional settings, you may want to consider using [`include`](../../#include-directive) in your main configuration, to reference the generated files. Otherwise, you can start a server and reference the generated configuration:
|
||||
|
||||
```text
|
||||
> nats-server -c /tmp/server.conf
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# resolver
|
||||
# Account lookup using Resolver
|
||||
|
||||
The `resolver` configuration option is used in conjunction with [NATS JWT Authentication](README.md) and [nsc](../../nats-tools/nsc/nsc). The `resolver` option specifies a URL where the nats-server can retrieve an account JWT. There are two built-in resolver implementations:
|
||||
The `resolver` configuration option is used in conjunction with [NATS JWT Authentication](./) and [nsc](https://github.com/nats-io/nats.docs/tree/aecb86faf9be946a413d1c6200fc0ff5d1b0baef/nats-server/configuration/nats-tools/nsc/nsc/README.md). The `resolver` option specifies a URL where the nats-server can retrieve an account JWT. There are two built-in resolver implementations:
|
||||
|
||||
* `URL`
|
||||
* `MEMORY`
|
||||
|
||||
- `URL`
|
||||
- `MEMORY`
|
||||
|
||||
> If the operator JWT specified in `operator` contains an account resolver URL, `resolver` only needs to be specified in order to overwrite that default.
|
||||
|
||||
## URL Resolver
|
||||
|
||||
The `URL` resolver specifies a URL where the server can append an account public key to retrieve that account's JWT. Convention for [NATS Account JWT Servers](../../nats-tools/nas) is to serve JWTs at: `http://localhost:9090/jwt/v1/accounts/`. For such a configuration you would specify the resolver as follows:
|
||||
The `URL` resolver specifies a URL where the server can append an account public key to retrieve that account's JWT. Convention for [NATS Account JWT Servers](https://github.com/nats-io/nats.docs/tree/aecb86faf9be946a413d1c6200fc0ff5d1b0baef/nats-server/configuration/nats-tools/nas/README.md) is to serve JWTs at: `http://localhost:9090/jwt/v1/accounts/`. For such a configuration you would specify the resolver as follows:
|
||||
|
||||
```yaml
|
||||
resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||
@@ -17,8 +17,7 @@ resolver: URL(http://localhost:9090/jwt/v1/accounts/)
|
||||
|
||||
> Note that if you are not using a nats-account-server, the URL can be anything as long as by appending the public key for an account, the requested JWT is returned.
|
||||
|
||||
If the server used requires client authentication, or you want to specify which CA is trusted for the lookup of account information, specify `resolver_tls`.
|
||||
This [`tls` configuration map](securing_nats/tls.md) lets you further restrict TLS to the resolver.
|
||||
If the server used requires client authentication, or you want to specify which CA is trusted for the lookup of account information, specify `resolver_tls`. This [`tls` configuration map](https://github.com/nats-io/nats.docs/tree/aecb86faf9be946a413d1c6200fc0ff5d1b0baef/nats-server/configuration/securing_nats/jwt/securing_nats/tls.md) lets you further restrict TLS to the resolver.
|
||||
|
||||
## MEMORY
|
||||
|
||||
@@ -34,3 +33,4 @@ ACSU3Q6LTLBVLGAQUONAGXJHVNWGSKKAUA7IY5TB4Z7PLEKSR5O6JTGR: eyJ0eXAiOiJqd3QiLCJhbG
|
||||
The `MEMORY` resolver is recommended when the server has a small number of accounts that don't change very often.
|
||||
|
||||
For more information on how to configure a memory resolver, see [this tutorial](mem_resolver.md).
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ The NATS server uses modern TLS semantics to encrypt client, route, and monitori
|
||||
| `cipher_suites` | When set, only the specified TLS cipher suites will be allowed. Values must match the golang version used to build the server. |
|
||||
| `curve_preferences` | List of TLS cipher curves to use in order. |
|
||||
| `insecure` | Skip certificate verification. **NOT Recommended** |
|
||||
| `timeout` | TLS handshake [timeout](#TLS-Timeout) in fractional seconds. Default set to `0.5` seconds. |
|
||||
| `timeout` | TLS handshake [timeout](tls.md#TLS-Timeout) in fractional seconds. Default set to `0.5` seconds. |
|
||||
| `verify` | If `true`, require and [verify](auth_intro/tls_mutual_auth.md#Validating-a-Client-Certificate) client certificates. To support use by Browser, this option does not apply to monitoring. |
|
||||
| `verify_and_map` | If `true`, require and verify client certificates and [map](auth_intro/tls_mutual_auth.md#Mapping-Client-Certificates-To-A-User) certificate values for authentication purposes. Does not apply to monitoring either. |
|
||||
|
||||
@@ -61,68 +61,53 @@ tls: {
|
||||
|
||||
## Self Signed Certificates for Testing
|
||||
|
||||
Explaining [Public key infrastructure](https://en.wikipedia.org/wiki/Public_key_infrastructure), [Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority) and [x509](https://tools.ietf.org/html/rfc5280) [certificates](https://en.wikipedia.org/wiki/Public_key_certificate) fall well outside the scope of this document.
|
||||
So does an explanation on how to obtain a properly trusted certificates.
|
||||
Explaining [Public key infrastructure](https://en.wikipedia.org/wiki/Public_key_infrastructure), [Certificate Authorities \(CA\)](https://en.wikipedia.org/wiki/Certificate_authority) and [x509](https://tools.ietf.org/html/rfc5280) [certificates](https://en.wikipedia.org/wiki/Public_key_certificate) fall well outside the scope of this document. So does an explanation on how to obtain a properly trusted certificates.
|
||||
|
||||
If anybody outside your organization needs to connect, get certs from a public certificate authority.
|
||||
Think carefully about revocation and cycling times, as well as automation, when picking a CA.
|
||||
If arbitrary applications inside your organization need to connect, use a cert from your in-house CA.
|
||||
If only resources inside a specific environment need to connect, that environment might have its own dedicated automatic CA, eg in Kubernetes clusters, so use that.
|
||||
If anybody outside your organization needs to connect, get certs from a public certificate authority. Think carefully about revocation and cycling times, as well as automation, when picking a CA. If arbitrary applications inside your organization need to connect, use a cert from your in-house CA. If only resources inside a specific environment need to connect, that environment might have its own dedicated automatic CA, eg in Kubernetes clusters, so use that.
|
||||
|
||||
**Only** for **testing** purposes does it makes sense to generate self signed certificates, even your own CA.
|
||||
This is a **short** guide on how to do just that and what to watch out for.
|
||||
**Only** for **testing** purposes does it makes sense to generate self signed certificates, even your own CA. This is a **short** guide on how to do just that and what to watch out for.
|
||||
|
||||
> **DO NOT USE these certificates in production!!!**
|
||||
|
||||
### Problems With Self Signed Certificates
|
||||
|
||||
#### Missing in Relevant Trust Stores
|
||||
#### Missing in Relevant Trust Stores
|
||||
|
||||
As they should, these are **not trusted** by the system your server or clients are running on.
|
||||
|
||||
One option is to specify the CA in every client you are using.
|
||||
In case you make use of `verify` and `verify_and_map` you need to specify `ca_file` in the server.
|
||||
If you are having a more complex setup involving cluster, gateways or leaf nodes, `ca_file` needs to be present in `tls` maps used to connect to the server with self signed certificates.
|
||||
While this works for server and libraries from the NATS eco system, you will experience issues when connecting with other tools such as your Browser.
|
||||
One option is to specify the CA in every client you are using. In case you make use of `verify` and `verify_and_map` you need to specify `ca_file` in the server. If you are having a more complex setup involving cluster, gateways or leaf nodes, `ca_file` needs to be present in `tls` maps used to connect to the server with self signed certificates. While this works for server and libraries from the NATS eco system, you will experience issues when connecting with other tools such as your Browser.
|
||||
|
||||
Another option is to configure your system's trust store to include self signed certificate\(s\). Which trust store needs to be configured depends on what you are testing.
|
||||
|
||||
Another option is to configure your system's trust store to include self signed certificate(s).
|
||||
Which trust store needs to be configured depends on what you are testing.
|
||||
* This may be your OS for server and certain clients.
|
||||
* The runtime environment for other clients like Java, Python or Node.js.
|
||||
* Your browser for monitoring endpoints and websockets.
|
||||
|
||||
Please check your system's documentation on how to trust a particular self signed certificate.
|
||||
|
||||
#### Missing Subject Alternative Name
|
||||
#### Missing Subject Alternative Name
|
||||
|
||||
Another common problem is failed [identity validation](https://tools.ietf.org/html/rfc6125).
|
||||
The IP or DNS name to connect to needs to match a [Subject Alternative Name (SAN)](https://tools.ietf.org/html/rfc4985) inside the certificate.
|
||||
Meaning, if a client/browser/server connect via tls to `127.0.0.1`, the server needs to present a certificate with a SAN containing the IP `127.0.0.1` or the connection will be closed with a handshake error.
|
||||
Another common problem is failed [identity validation](https://tools.ietf.org/html/rfc6125). The IP or DNS name to connect to needs to match a [Subject Alternative Name \(SAN\)](https://tools.ietf.org/html/rfc4985) inside the certificate. Meaning, if a client/browser/server connect via tls to `127.0.0.1`, the server needs to present a certificate with a SAN containing the IP `127.0.0.1` or the connection will be closed with a handshake error.
|
||||
|
||||
#### Wrong Key Usage
|
||||
|
||||
When generating your certificate you need to make sure to include the right purpose for which you want to use the certificate.
|
||||
This is encoded in [key usage](https://tools.ietf.org/html/rfc5280#section-4.2.1.3) and [extended key usage](https://tools.ietf.org/html/rfc5280#section-4.2.1.12).
|
||||
The necessary values for key usage depend on the ciphers used. `Digital Signature` and `Key Encipherment` are an interoperable choice.
|
||||
When generating your certificate you need to make sure to include the right purpose for which you want to use the certificate. This is encoded in [key usage](https://tools.ietf.org/html/rfc5280#section-4.2.1.3) and [extended key usage](https://tools.ietf.org/html/rfc5280#section-4.2.1.12). The necessary values for key usage depend on the ciphers used. `Digital Signature` and `Key Encipherment` are an interoperable choice.
|
||||
|
||||
With respect to NATS the relevant values for extended key usage are:
|
||||
|
||||
* `TLS WWW server authentication` - To authenticate as server for incoming connections. A NATS server will need a certificate containing this.
|
||||
* `TLS WWW client authentication` - To authenticate as client for outgoing connections. Only needed when connecting to a server where `verify` or `verify_and_map` are specified. In these cases, a NATS client will need a certificate with this value.
|
||||
* [Leaf node](../leafnodes/README.md) connections can be configured with `verify` as well. Then connecting NATS server will have to present a certificate with this value too. Certificates containing both values are an option.
|
||||
* [Cluster](../clustering/README.md) connections always have `verify` enabled. Which server acts as client and server comes down to timing and therefore can't be individually configured. Certificates containing both values are a must.
|
||||
* [Gateway](../gateways/README.md) connections always have `verify` enabled. Unlike cluster outgoing connections can specify a separate cert. Certificates containing both values are an option that reduce configuration.
|
||||
* [Leaf node](../leafnodes/) connections can be configured with `verify` as well. Then connecting NATS server will have to present a certificate with this value too. Certificates containing both values are an option.
|
||||
* [Cluster](../clustering/) connections always have `verify` enabled. Which server acts as client and server comes down to timing and therefore can't be individually configured. Certificates containing both values are a must.
|
||||
* [Gateway](../gateways/) connections always have `verify` enabled. Unlike cluster outgoing connections can specify a separate cert. Certificates containing both values are an option that reduce configuration.
|
||||
|
||||
Note that it's common practice for non-web protocols to use the `TLS WWW` authentication fields, as a matter of history those have become embedded as generic options.
|
||||
|
||||
### Creating Self Signed Certificates for Testing
|
||||
|
||||
The simplest way to generate a CA as well as client and server certificates is [mkcert](https://github.com/FiloSottile/mkcert).
|
||||
This zero config tool generates and installs the CA into your **local** system trust store(s) and makes providing SAN straight forward.
|
||||
Check it's [documentation](https://github.com/FiloSottile/mkcert/blob/master/README.md) for installation and your system's trust store.
|
||||
Here is a simple example:
|
||||
The simplest way to generate a CA as well as client and server certificates is [mkcert](https://github.com/FiloSottile/mkcert). This zero config tool generates and installs the CA into your **local** system trust store\(s\) and makes providing SAN straight forward. Check it's [documentation](https://github.com/FiloSottile/mkcert/blob/master/README.md) for installation and your system's trust store. Here is a simple example:
|
||||
|
||||
Generate a CA as well as a certificate, valid for server authentication by `localhost` and the IP `::1`(`-cert-file` and `-key-file` overwrite default file names).
|
||||
Then start a nats server using the generated certificate.
|
||||
Generate a CA as well as a certificate, valid for server authentication by `localhost` and the IP `::1`\(`-cert-file` and `-key-file` overwrite default file names\). Then start a nats server using the generated certificate.
|
||||
|
||||
```bash
|
||||
mkcert -install
|
||||
@@ -131,36 +116,27 @@ nats-server --tls --tlscert=server-cert.pem --tlskey=server-key.pem -ms 8222
|
||||
```
|
||||
|
||||
Now you should be able to access the monitoring endpoint `https://localhost:8222` with your browser.
|
||||
`https://127.0.0.1:8222` however should result in an error as `127.0.0.1` is not listed as SAN.
|
||||
You will not be able to establish a connection from another computer either.
|
||||
For that to work you have to provide appropriate DNS and/or IP [SAN(s)](#Missing-Subject-Alternative-Name)
|
||||
`https://127.0.0.1:8222` however should result in an error as `127.0.0.1` is not listed as SAN. You will not be able to establish a connection from another computer either. For that to work you have to provide appropriate DNS and/or IP [SAN\(s\)](tls.md#Missing-Subject-Alternative-Name)
|
||||
|
||||
To generate certificates that work with `verify` provide the `-client` option.
|
||||
This will cause it to add an appropriate key usage for client authentication.
|
||||
Please note that client refers to connecting process, not necessarily a NATS client.
|
||||
Also add a SAN email for usage as user name in `verify_and_map`.
|
||||
To generate certificates that work with `verify` provide the `-client` option. This will cause it to add an appropriate key usage for client authentication. Please note that client refers to connecting process, not necessarily a NATS client. Also add a SAN email for usage as user name in `verify_and_map`.
|
||||
|
||||
```bash
|
||||
mkcert -client -cert-file client-cert.pem -key-file client-key.pem localhost ::1 email@localhost
|
||||
```
|
||||
|
||||
Examples in this document make use of the certificates generated so far.
|
||||
To simplify examples using the CA certificate, copy `rootCA.pem` into the same folder where the certificates were generated.
|
||||
To obtain the CA certificate's location use this command:
|
||||
Examples in this document make use of the certificates generated so far. To simplify examples using the CA certificate, copy `rootCA.pem` into the same folder where the certificates were generated. To obtain the CA certificate's location use this command:
|
||||
|
||||
```bash
|
||||
mkcert -CAROOT
|
||||
```
|
||||
|
||||
Once you are done testing, remove the CA from your **local** system trust store(s).
|
||||
Once you are done testing, remove the CA from your **local** system trust store\(s\).
|
||||
|
||||
```
|
||||
```text
|
||||
mkcert -uninstall
|
||||
```
|
||||
|
||||
Alternatively, you can also use [openssl](https://www.openssl.org/) to [generate certificates](https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs).
|
||||
This tool allows a lot more customization of the generated certificates.
|
||||
It is a lot **more complex** and does **not manage** installation into the system trust store(s).
|
||||
Alternatively, you can also use [openssl](https://www.openssl.org/) to [generate certificates](https://www.digitalocean.com/community/tutorials/openssl-essentials-working-with-ssl-certificates-private-keys-and-csrs). This tool allows a lot more customization of the generated certificates. It is a lot **more complex** and does **not manage** installation into the system trust store\(s\).
|
||||
|
||||
However, for inspecting certificates it is quite handy. To inspect the certificates from the above example execute these commands:
|
||||
|
||||
@@ -168,3 +144,4 @@ However, for inspecting certificates it is quite handy. To inspect the certifica
|
||||
openssl x509 -noout -text -in server-cert.pem
|
||||
openssl x509 -noout -text -in client-cert.pem
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user