Closed kimjarvis closed 1 year ago
add user
is an operation performed by the account owner - so effectively, yes they must have the account private key to issue an user. Neither the operator nor a different account can issue users.
The delegation model is operator issues accounts, accounts issue users. The nuance is that an account in self-service mode, must be self signed, the operator can then use one of its keys to re-issue the account signed by the operator key.
I think the main issue here is a workflow issue, here's the correct workflow:
# Here's the operator steps
> ~ [1]> nsc add operator O
[ OK ] generated and stored operator key "OB6ATHSVPF767UH7JQJO6YQ6CXELOKXEBLX2M4Q6VLJ4HP4A3TIJYHDR"
[ OK ] added operator "O"
[ OK ] When running your own nats-server, make sure they run at least version 2.2.0
> ~> nsc env
+----------------------------------------------------------------------------------------------------------+
| NSC Environment |
+--------------------+-----+-------------------------------------------------------------------------------+
| Setting | Set | Effective Value |
+--------------------+-----+-------------------------------------------------------------------------------+
| $NSC_CWD_ONLY | No | If set, default operator/account from cwd only |
| $NSC_NO_GIT_IGNORE | No | If set, no .gitignore files written |
| $NKEYS_PATH | No | ~/.local/share/nats/nsc/keys |
| $NSC_HOME | No | ~/.config/nats/nsc |
| $NATS_CA | No | If set, root CAs in the referenced file will be used for nats connections |
| | | If not set, will default to the system trust store |
| $NATS_KEY | No | If set, the tls key in the referenced file will be used for nats connections |
| $NATS_CERT | No | If set, the tls cert in the referenced file will be used for nats connections |
+--------------------+-----+-------------------------------------------------------------------------------+
| From CWD | | No |
| Default Stores Dir | | ~/.local/share/nats/nsc/stores |
| Current Store Dir | | ~/.local/share/nats/nsc/stores |
| Current Operator | | O |
| Current Account | | |
| Root CAs to trust | | Default: System Trust Store |
+--------------------+-----+-------------------------------------------------------------------------------+
# make the operator JWT available (an account server does this automagically)
> ~> cp ~/.local/share/nats/nsc/stores/O/O.jwt /tmp/O.jwt
# renaming the location where nsc is storing data, so that we simulate a clean environment
> ~> mv ~/.local/share/nats/nsc ~/.local/share/nats/nsc_demo
# add the operator by importing it from a file or url
> ~> nsc add operator -u /tmp/O.jwt
[ OK ] imported operator "O"
[ OK ] When running your own nats-server, make sure they run at least version 2.2.0
# now we add an account - note that the account will be self signed, because the operator was imported
# internally maked the operator (.nsc file in the directory for the operator) as being managed
# {"managed":true,"name":"O","kind":"operator","version":"1"}⏎
# managed makes nsc self-sign accounts:
> ~> nsc add account A
[ OK ] generated and stored account key "ACBUHSTCUYBTB5LP4GJSSGKBMJO53SMV5L2C5WK3ZY3NNODZCBYHIORT"
[WARN] unable to push to "O" - operator doesn't set an account server url or manual exchange necessary
[WARN] perform push/pull again or exchange self-signed JWT manually
[ OK ] added account "A"
# note the issuer ID here is the account itself:
> ~> nsc describe account A
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | ACBUHSTCUYBTB5LP4GJSSGKBMJO53SMV5L2C5WK3ZY3NNODZCBYHIORT |
| Issuer ID | ACBUHSTCUYBTB5LP4GJSSGKBMJO53SMV5L2C5WK3ZY3NNODZCBYHIORT |
| Issued | 2023-04-18 14:43:25 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
| Exports | None |
+---------------------------+----------------------------------------------------------+
# now the user would need to provide their account JWT to the operator
# the operator would import it, the import process is effectively
# editing the account assigning limits, etc, and signing with the operator key
# to do this, import the account JWT into the operator's environment:
> ~> cp ~/.local/share/nats/nsc/stores/O/accounts/A/A.jwt /tmp/A.jwt
> ~> mv ~/.local/share/nats/nsc ~/.local/share/nats/nsc_user
> ~> mv ~/.local/share/nats/nsc_demo ~/.local/share/nats/nsc
> ~> nsc import account --file /tmp/A.jwt
[WARN] validation resulted in: self-signed account JWTs shouldn't contain operator limits
[ OK ] account A was successfully imported
1 job succeeded - 1 have warnings
# note for the operator the jwt is signed by the operator
> ~> nsc describe account A
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | ACBUHSTCUYBTB5LP4GJSSGKBMJO53SMV5L2C5WK3ZY3NNODZCBYHIORT |
| Issuer ID | OB6ATHSVPF767UH7JQJO6YQ6CXELOKXEBLX2M4Q6VLJ4HP4A3TIJYHDR |
| Issued | 2023-04-18 14:45:03 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
| Exports | None |
+---------------------------+----------------------------------------------------------+
This document may be super helpful in understanding the delegated authentication model
https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt
@kimjarvis I am closing all these issues, as I believe the missing aspect was how to onboard an operator in a managed
environment. If the above is not clear, please feel free to open a new issue or reach out on slack.
Thank you Alberto for your clear explanation. Continuing the above we can generate a user and subscribe to a stream (I generated the operator with a system account).
root@instance:/# nsc push -A -u nats://localhost:4222
[ OK ] push to nats-server "nats://localhost:4222" using system account "SYS":
[ OK ] push A to nats-server with nats account resolver:
[ OK ] pushed "A" to nats-server NADUSVVVWUEP7WBKYSFVU4GVTNTDVEDGUQQRY63II6NPTK6E6GIUTIJ7: jwt updated
[ OK ] pushed to a total of 1 nats-server
[ OK ] push SYS to nats-server with nats account resolver:
[ OK ] pushed "SYS" to nats-server NADUSVVVWUEP7WBKYSFVU4GVTNTDVEDGUQQRY63II6NPTK6E6GIUTIJ7: jwt updated
[ OK ] pushed to a total of 1 nats-server
mv ~/.local/share/nats/nsc ~/.local/share/nats/nsc_demo
mv ~/.local/share/nats/nsc_user ~/.local/share/nats/nsc
root@instance:/# nsc add user -a A -n U
[ OK ] generated and stored user key "UCLZ4TFWIIOGZAPL4ZV23I7G64CD7KH2PPAI6CLMJU2ZG2OAMTL4JZVR"
[ OK ] generated user creds file `~/.local/share/nats/nsc/keys/creds/O/A/U.creds`
[ OK ] added user "U" to account "A"
root@instance:/# nsc generate creds -a A -n U -o U.creds
[ OK ] wrote credentials to `U.creds`
Success!! - generated `U.creds`
root@instance:/# nats --creds U.creds -s nats://localhost:4222 sub foo
14:31:10 Subscribing on foo
We can add an export. We observe that the account still appears to be self signed. Which is expected.
root@instance:/# nsc add export --name base --subject "base.>" --account A
[WARN] unable to push to "O" - operator doesn't set an account server url or manual exchange necessary
[WARN] perform push/pull again or exchange self-signed JWT manually
[ OK ] added public stream export "base"
root@instance:/# nsc describe account
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | AD6HZFK5O56HMUZKI2XTD4OCETQETFQ4CLFHK5UZJLJ4RWSXPY7U3GVB |
| Issuer ID | AD6HZFK5O56HMUZKI2XTD4OCETQETFQ4CLFHK5UZJLJ4RWSXPY7U3GVB |
| Issued | 2023-04-22 14:33:54 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
+---------------------------+----------------------------------------------------------+
+-----------------------------------------------------------+
| Exports |
+------+--------+---------+--------+-------------+----------+
| Name | Type | Subject | Public | Revocations | Tracking |
+------+--------+---------+--------+-------------+----------+
| base | Stream | base.> | Yes | 0 | N/A |
+------+--------+---------+--------+-------------+----------+
We export the account JWT and import it to the administrator, where it appears signed by the operator. We observe that the export has been carried across. We can push it to the server.
root@instance:/# cp ~/.local/share/nats/nsc/stores/O/accounts/A/A.jwt /tmp/A.jwt
root@instance:/# mv ~/.local/share/nats/nsc ~/.local/share/nats/nsc_user
root@instance:/# mv ~/.local/share/nats/nsc_demo ~/.local/share/nats/nsc
root@instance:/# nsc import account --file /tmp/A.jwt --overwrite
[WARN] validation resulted in: self-signed account JWTs shouldn't contain operator limits
[ OK ] account A was successfully imported
1 job succeeded - 1 have warnings
root@instance:/# nsc describe account A
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | AD6HZFK5O56HMUZKI2XTD4OCETQETFQ4CLFHK5UZJLJ4RWSXPY7U3GVB |
| Issuer ID | OCO2OS44LKIKE44ICURZ5XPAXQ7A5A4FOBFFGKRUIJZI4GLBTRMU5TJ6 |
| Issued | 2023-04-22 14:36:24 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
+---------------------------+----------------------------------------------------------+
+-----------------------------------------------------------+
| Exports |
+------+--------+---------+--------+-------------+----------+
| Name | Type | Subject | Public | Revocations | Tracking |
+------+--------+---------+--------+-------------+----------+
| base | Stream | base.> | Yes | 0 | N/A |
+------+--------+---------+--------+-------------+----------+
root@instance:/# nsc push -A -u nats://localhost:4222
[ OK ] push to nats-server "nats://localhost:4222" using system account "SYS":
[ OK ] push A to nats-server with nats account resolver:
[ OK ] pushed "A" to nats-server NADUSVVVWUEP7WBKYSFVU4GVTNTDVEDGUQQRY63II6NPTK6E6GIUTIJ7: jwt updated
[ OK ] pushed to a total of 1 nats-server
[ OK ] push SYS to nats-server with nats account resolver:
[ OK ] pushed "SYS" to nats-server NADUSVVVWUEP7WBKYSFVU4GVTNTDVEDGUQQRY63II6NPTK6E6GIUTIJ7: jwt updated
[ OK ] pushed to a total of 1 nats-server
It works fine and I really appreciate your clear explanation and taking the time to write this example.
However, the example does not address the problem described in the ticket. This ticket is about signing keys. The procedure outlined above, and my continuation of it, does not use signing keys.
When the operator is created with "--generate-signing-key" and edited to add "--require-signing-keys" the above process works very similarly. When the account JWT is imported the first operator signing key is used to sign the account. This appears to be the account issuer as expected.
root@instance:/# nsc describe account A
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | ABWTOPA2OBQ2LQRXT2SHWSPF6RBEWSUZ6TBD3NIIGPGT2X7CWT7RFY3O |
| Issuer ID | OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK |
| Issued | 2023-04-22 15:05:59 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
| Exports | None |
+---------------------------+----------------------------------------------------------+
root@instance:/# nsc describe operator
+----------------------------------------------------------------------------------------+
| Operator Details |
+-----------------------+----------------------------------------------------------------+
| Name | O |
| Operator ID | OBO4JQ5HAEEK7BEZPI3MG6GUX3YRJOISH2W7JAGAVK7GHXQUMCKXLOJY |
| Issuer ID | OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK |
| Issued | 2023-04-22 15:03:05 UTC |
| Expires | |
| Account JWT Server | nats://localhost:4222 |
| Operator Service URLs | nats://localhost:4222 |
| System Account | ACKIRY4U6GQDCU2AMUIJZ5AULKILJSN3IBBEXWNN3H7ZP6JR6JC2LYGT / SYS |
| Require Signing Keys | true |
+-----------------------+----------------------------------------------------------------+
| Signing Keys | OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK |
+-----------------------+----------------------------------------------------------------+
However, when we try add a user on the client side the signing key cannot be found. The message is slightly different from the one in the original post, because the process is different. However the problem is the same. nsc add user
is attempting to validate signing keys but it does not have access to them.
root@instance:/# mv ~/.local/share/nats/nsc ~/.local/share/nats/nsc_demo
root@instance:/# mv ~/.local/share/nats/nsc_user ~/.local/share/nats/nsc
root@instance:/# nsc describe operator
+----------------------------------------------------------------------------------+
| Operator Details |
+-----------------------+----------------------------------------------------------+
| Name | O |
| Operator ID | OBO4JQ5HAEEK7BEZPI3MG6GUX3YRJOISH2W7JAGAVK7GHXQUMCKXLOJY |
| Issuer ID | OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK |
| Issued | 2023-04-22 15:03:05 UTC |
| Expires | |
| Account JWT Server | nats://localhost:4222 |
| Operator Service URLs | nats://localhost:4222 |
| System Account | ACKIRY4U6GQDCU2AMUIJZ5AULKILJSN3IBBEXWNN3H7ZP6JR6JC2LYGT |
| Require Signing Keys | true |
+-----------------------+----------------------------------------------------------+
| Signing Keys | OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK |
+-----------------------+----------------------------------------------------------+
root@instance:/# nsc describe account A
+--------------------------------------------------------------------------------------+
| Account Details |
+---------------------------+----------------------------------------------------------+
| Name | A |
| Account ID | ABWTOPA2OBQ2LQRXT2SHWSPF6RBEWSUZ6TBD3NIIGPGT2X7CWT7RFY3O |
| Issuer ID | ABWTOPA2OBQ2LQRXT2SHWSPF6RBEWSUZ6TBD3NIIGPGT2X7CWT7RFY3O |
| Issued | 2023-04-22 15:05:01 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 |
| Disallow Bearer Token | False |
| Response Permissions | Not Set |
+---------------------------+----------------------------------------------------------+
| Jetstream | Disabled |
+---------------------------+----------------------------------------------------------+
| Imports | None |
| Exports | None |
+---------------------------+----------------------------------------------------------+
root@instance:/# nsc add user -a A -n U
Error: unable to resolve any of the following signing keys in the keystore:
root@instance:/#
The error message does not list the signing keys that it is looking for. However, nsc add user
does, somehow, know that the operator requires signing keys.
When we try to add a signing key on the client side we cannot do it because the operator private key is not available.
root@instance:/# nsc generate nkey -a --store
AAUR6RSMK7NM6N4RDL7YNUSEPJLQTHPAZPAYRGWCMIOUNG7BADWO64IA
account key stored /root/.local/share/nats/nsc/keys/keys/A/AU/AAUR6RSMK7NM6N4RDL7YNUSEPJLQTHPAZPAYRGWCMIOUNG7BADWO64IA.nk
root@instance:/# nsc edit account -n A --sk AAUR6RSMK7NM6N4RDL7YNUSEPJLQTHPAZPAYRGWCMIOUNG7BADWO64IA
Error: unable to resolve any of the following signing keys in the keystore: OARI4XVD466PIZNLG5QJEVLF7ZQML75MCWJHBP2L2NGN2VZ2BOFDKEDK
It is possible to add a signing key to the account on the administrator side and then export the account (like we do for system accounts). But, that leaves the administrator with the private part of the client's account signing key, which we want to avoid in the self service model.
nsc add user
attempts to validate the account nkeys in the local keystore, but in the self service deployment model the local keystore does not contain the private key of the account.In the self service model, the administrator creates an account for the client to import. The client first import the operator JWT, then the account JWT. In the self service model, the account nkeys are not available in the clients's local keystore. The operator and account nkeys are private to to the administrator. The
nsc add user
command should not attempt to validate the account nkey in the local keystore because they may not be present.The client imported the operator and the account from the administrator.
The client has only the local system user nkeys in its keystore. The operator and account nkeys are not present.
For reference, the administrator keystore contains the operator and account nkeys: