Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[node] Add NATS auth #325

Open
rhlsthrm opened this issue Sep 16, 2019 · 14 comments

Comments

@rhlsthrm
Copy link
Member

@rhlsthrm rhlsthrm commented Sep 16, 2019

We need to auth the NATS endpoints especially the lock service calls.

@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Sep 16, 2019

Something along the lines of this to generate a certificate authority and begin issuing client certs:

# create certificate authority
openssl genrsa -des3 -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.pem

# create ca-signed client cert
openssl genrsa -out peer.key 4096
openssl req -new -key peer.key -out peer.csr
openssl x509 -req -in peer.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out peer.crt -days 365 -sha256 -extfile peer.ext
@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Sep 16, 2019

Need to look into how we support client auth for a plurality of certificates.

@bohendo

This comment has been minimized.

Copy link
Member

@bohendo bohendo commented Sep 17, 2019

Any random client should be allowed to send messages & interact w their own stuff by default where "their own stuff" is defined by the key pair they control (verified w a sig) compared to some multisig's owners or some app instances participants. Some of the subjects eg config.get should be publicly available for anyone to query too. I don't understand what problem a cert authority would be solving here though @kthomas

@hthillman

This comment has been minimized.

Copy link
Member

@hthillman hthillman commented Sep 18, 2019

Any update here @bohendo?

@rhlsthrm

This comment has been minimized.

Copy link
Member Author

@rhlsthrm rhlsthrm commented Sep 19, 2019

I think we need to chat with @kthomas and scope this out.

@bohendo bohendo added this to the Prep for v1 Depreciation milestone Sep 19, 2019
@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Sep 21, 2019

Let's schedule some time to discuss 👍

@bohendo

This comment has been minimized.

Copy link
Member

@bohendo bohendo commented Sep 24, 2019

Tentative auth flow:

  1. New client wakes up
  2. Client sends nats message w subject config
  3. config subject is open to the public so the node responds w config info
  4. Client sends nats message w subject channel.create.*
  5. channel.* messages are protected so this message is either not propagated or ignored by hub
  6. Client asks node to authenticate
  7. Node gives client a nonce to sign
  8. Client signs & gives sig to node
  9. Node checks sig, if it's good it issues a cert to the client
  10. Client tries to send message w channel.create.* subject again
  11. Hub checks cert & verifies this client is allowed to access this subject so it responds

This sounds like what you're thinking @kthomas?

@ArjunBhuptani

This comment has been minimized.

Copy link
Member

@ArjunBhuptani ArjunBhuptani commented Sep 24, 2019

Part of #334 meta-issue

@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Sep 25, 2019

Tentative auth flow:

  1. New client wakes up
  2. Client sends nats message w subject config
  3. config subject is open to the public so the node responds w config info
  4. Client sends nats message w subject channel.create.*
  5. channel.* messages are protected so this message is either not propagated or ignored by hub
  6. Client asks node to authenticate
  7. Node gives client a nonce to sign
  8. Client signs & gives sig to node
  9. Node checks sig, if it's good it issues a cert to the client
  10. Client tries to send message w channel.create.* subject again
  11. Hub checks cert & verifies this client is allowed to access this subject so it responds

This sounds like what you're thinking @kthomas?

@bohendo thanks for sharing this, insightful and good context for me as I am beginning to research NATS options re: client auth vs. vending signed JWT.

I am particularly curious as to how to protect NATS directly vs. depending on the hub as an authorization proxy, as that implies our solution would not scale to support a future iteration when NATS looks more like a mesh network. I am hoping we can architect this such that the hub will indeed be responsible for signing credentials (i.e., during a config or similar step as you describe), but the NATS instance will be able to dynamically use those credentials. So, that is to say, item #11 in your list above breaks down as we actually need NATS to be aware of our authentication and authorization strategy (authentication meaning yes, that user is valid and authorization meaning the authenticated user has sufficient permission to interact with a whitelist of subjects). Bonus points here if we can authorize publish and subscribe independently on NATS subjects. I have doubts that the authentication facilities are that robust just yet... hoping to find that out shortly.

Updates forthcoming.

@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Sep 25, 2019

Update:

NATS actually has support for subject-level authorization and it is implemented with the granularity we need... and nearly exactly as we discussed (see https://nats-io.github.io/docs/nats_server/authorization.html). Awesome...

The approach is to allow the hub to vend JWT tokens for users upon request, signing the JWT using the RS256 algorithm. NATS will be aware of the public key needed to verify the signed JWT so it can authenticate the user and trust the authorized scope presented in the payload (i.e. publish/subscribe privileges, per subject id). This is not yet an authentication strategy supported by NATS.

I am going to make a NATS PR that adds support for an arbitrary JWT to be presented by clients for authentication and authorization. NATS server will use the hub's public key to authenticate the user and verify that it can trust the embedded authorization.

Next update should include a link to that PR.

@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Oct 1, 2019

For posterity as a lot of discussion happened earlier... current status of this:

As per our discussion this morning, the PR for adding support for bearer JWT authorization to NATS server is going to require proper due diligence to get it ready for review. There is an appetite for this authorization strategy among the creators/core maintainers of NATS so getting a proper PR ready for review will take another week or two but it is well worth it to target possible inclusion in the next release of NATS. While that PR is pending and possibly even prior to the first release of NATS which includes our prospective functionality, we will maintain a fork.

When running the NATS server fork, enabling JWT bearer authorization will simply require the presence of a JWT_SIGNER_PUBLIC_KEY in the environment. Bearer tokens signed with the private key (i.e., by the hub) will present the following in the JWT payload: {"data": {"permissions": "publish": [], "subscribe": [] }}. See NATS Authorization docs for valid permissions options. In other words: in this model the hub is delegating authorization to NATS server when it vends a JWT signed by its private key.

Our bearer tokens will have a short ttl (i.e., exp default to something like +1 hour from the iat issued at timestamp; i.e., exp := Date.now() + 3600000). NATS clients will be responsible for gracefully "refreshing" tokens prior to expiration of the previously signed bearer token and using the latest valid credentials upon reconnect.

After the above strategy works, we need to circle back and support (i) TLS and (ii) the use of our NATS connections with NATS streaming server to enable message durability and encryption at rest.

I will add a link to the testing branch I have been spiking as soon as it's ready-ish.

@bohendo

@hthillman hthillman moved this from To do to In progress in Sprint Task Workboard Oct 1, 2019
@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Oct 3, 2019

Here is a nats-server branch that we can use for testing: https://github.com/kthomas/nats-server/tree/jwt-bearer-auth

@bohendo reach out to me before you get started to confirm which sha to use.

@kthomas

This comment has been minimized.

Copy link
Collaborator

@kthomas kthomas commented Oct 3, 2019

The jwt-bearer-auth spike branch is ready for testing:

The nats-server docker image needs to be rebuilt after cloning the repo. Make sure to setup the docker run invocation to include a JWT_SIGNER_PUBLIC_KEY environment variable (the branch panics if it isn't set).

A connection to NATS will fail to be authenticated and authorized under the following conditions:

  • when no bearer token is provided by the user
  • when the bearer token is provided but fails signature verification based on JWT_SIGNER_PUBLIC_KEY
  • when the bearer token is verified but no permissions claim exists in the JWT

I have not had a chance yet to test when well- or poorly-formed permissions are presented within the token but I am about to write a bunch of tests and get ts-natsutil (just a convenience wrapper, in the spirit of go-natsutil) working with some examples. That wrapper project will also provide working examples of NATS streaming.

Proof of life:

➜  nats-server git:(jwt-bearer-auth) JWT_SIGNER_PUBLIC_KEY='
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0yqdJMBej1X8WwMMkMZw
DV6zZzup4RHLcln0xfGSm6dMPBDM1G96fuHhOwH5+uU5MQHJP7RqW71Bu5dLIG8Z
RX+XyUtb0sxCV/7X27Nm/bKpDysaSWQ36reAmw5wVaB1SoFeN519FY5rhoCWmH3W
auBAHTzpjg57p7uR0XynYXf8NSGXlysWHppkppqwrPH64G6UZaB7SMl1PFfkJeqZ
zJpzBGYWsixdF1EjXn+Yz0mhUZO2OSPWifOuN7cpn3BuNqegg4iVdz5HDoQhJW7N
uRhf3buKd/mjat8XA3e2Rkrr2h835GloScJkj7I4BZUNkzKQuEK6C9xW/zJtbPqQ
RYEq84A1hMfSZ3G5HFe2JkqiyvXkFwS3qMc5Pur8tZSzBj6AYMoJJso/aOdphpR8
6MaaWXWTwvwfpZbMRqehOcsmQcNLF2gLJPuHzR5WtVCnWrDgvjsWyeDD1WISKusi
aOeHxZjS3Bjl4Imq48l1wi2eI/11F/Xg70F4FJaMYLVHJA2nsmBuuQ9UDYHHq876
clKvIvgIItzJcv9lnmjl1Jks1DwCUF3qF2ugYcs9A3EoEcNzhMgZNJ2j5OUzfx1E
bzVKkqoC9MQpZWXgqV0KQqKK4I3rMY+1hLqk4S4eF9ZAVlT33qfMzlf0qWTOcP1Z
i2dsm0fy4NxWxknlEn5/LhMCAwEAAQ==
-----END PUBLIC KEY-----
' ./nats-server -p 4220 -DV
[94862] 2019/10/03 03:26:26.655536 [INF] Starting nats-server version 2.1.0
[94862] 2019/10/03 03:26:26.655621 [DBG] Go build version go1.12.6
[94862] 2019/10/03 03:26:26.655624 [INF] Git commit [not set]
[94862] 2019/10/03 03:26:26.655883 [INF] Listening for client connections on 0.0.0.0:4220
[94862] 2019/10/03 03:26:26.655892 [INF] Server id is NDNXMA3XP5NHR3LJX4VFDR6HHHYIWG3HYB6GMMWQOM2KWCA45S24UCUU
[94862] 2019/10/03 03:26:26.655897 [INF] Server is ready
[94862] 2019/10/03 03:26:26.655904 [DBG] Get non local IPs for "0.0.0.0"
[94862] 2019/10/03 03:26:26.656103 [DBG]  ip=192.168.254.79
[94862] 2019/10/03 03:26:31.073247 [DBG] 127.0.0.1:58833 - cid:1 - Client connection created
[94862] 2019/10/03 03:26:31.091261 [TRC] 127.0.0.1:58833 - cid:1 - <<- [CONNECT {"lang":"typescript","version":"1.2.4","verbose":false,"pedantic":false,"protocol":1,"name":"ts-natsutil-1976e59a-b9c4-4124-b96b-0ad68683eac2","jwt":"<redacted>"}]
Parsed bearer authorization header as valid JWT: map[data:map[] iat:%!s(float64=1.564572724e+09) jti:aa64f0ed-631d-4fcf-be56-670307821c62 sub:application:57f8c0af-089a-4ab3-b3c2-ca8a9ed547e0]
; client authentication: 127.0.0.1:58833 - cid:1
WARN: no permissions claim present in verified JWT
[94862] 2019/10/03 03:26:31.091762 [ERR] 127.0.0.1:58833 - cid:1 - authentication error
[94862] 2019/10/03 03:26:31.091771 [TRC] 127.0.0.1:58833 - cid:1 - ->> [-ERR Authorization Violation]
[94862] 2019/10/03 03:26:31.091801 [DBG] 127.0.0.1:58833 - cid:1 - Client connection closed
@ArjunBhuptani ArjunBhuptani moved this from In progress to To do in Sprint Task Workboard Oct 15, 2019
@ArjunBhuptani ArjunBhuptani moved this from To do to Icebox (unfiltered, not in this sprint) in Sprint Task Workboard Oct 15, 2019
@ArjunBhuptani ArjunBhuptani removed this from the Devcon milestone Oct 16, 2019
@bohendo bohendo removed this from Icebox (unfiltered, not in this sprint) in Sprint Task Workboard Oct 18, 2019
@rhlsthrm

This comment has been minimized.

Copy link
Member Author

@rhlsthrm rhlsthrm commented Nov 14, 2019

What more needs to be done with this @bohendo @kthomas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Devcon
  
To do
5 participants
You can’t perform that action at this time.