Managing NATS Security

If you are using the JWT model of authentication to secure your NATS infrastructure or implementing an Auth callout service, you can administer authentication and authorization without having to change the servers’ configuration files.

You can use the nsc CLI tool to manage identities. Identities take the form of nkeys. Nkeys are a public-key signature system based on Ed25519 for the NATS ecosystem. The nkey identities are associated with NATS configuration in the form of a JSON Web Token (JWT). The JWT is digitally signed by the private key of an issuer forming a chain of trust. The nsc tool creates and manages these identities and allows you to deploy them to a JWT account server, which in turn makes the configurations available to nats-servers.

You can also use nk CLI tool and library to manage keys.

Creating, updating and managing JWTs programmatically

You can create, update and delete accounts and users programmatically using the following libraries:

Integrating with your existing authentication/authorization system

You can integrate NATS with your existing authentication/authorization system or create your own custom authentication using the Auth callout.

Examples

See NATS by Example under “Authentication and Authorization” for JWT and Auth callout server implementation examples.

User JWTs

User creation in Golang

Account JWTs

Golang example from https://natsbyexample.com/examples/auth/nkeys-jwts/go

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "log"
  6. "github.com/nats-io/jwt/v2"
  7. "github.com/nats-io/nkeys"
  8. )
  9. func main() {
  10. log.SetFlags(0)
  11. var (
  12. accountSeed string
  13. operatorSeed string
  14. name string
  15. )
  16. flag.StringVar(&operatorSeed, "operator", "", "Operator seed for creating an account.")
  17. flag.StringVar(&accountSeed, "account", "", "Account seed for creating a user.")
  18. flag.StringVar(&name, "name", "", "Account or user name to be created.")
  19. flag.Parse()
  20. if accountSeed != "" && operatorSeed != "" {
  21. log.Fatal("operator and account cannot both be provided")
  22. }
  23. var (
  24. jwt string
  25. err error
  26. )
  27. if operatorSeed != "" {
  28. jwt, err = createAccount(operatorSeed, name)
  29. } else if accountSeed != "" {
  30. jwt, err = createUser(accountSeed, name)
  31. } else {
  32. flag.PrintDefaults()
  33. return
  34. }
  35. if err != nil {
  36. log.Fatalf("error creating account JWT: %v", err)
  37. }
  38. fmt.Println(jwt)
  39. }
  40. func createAccount(operatorSeed, accountName string) (string, error) {
  41. akp, err := nkeys.CreateAccount()
  42. if err != nil {
  43. return "", fmt.Errorf("unable to create account using nkeys: %w", err)
  44. }
  45. apub, err := akp.PublicKey()
  46. if err != nil {
  47. return "", fmt.Errorf("unable to retrieve public key: %w", err)
  48. }
  49. ac := jwt.NewAccountClaims(apub)
  50. ac.Name = accountName
  51. // Load operator key pair
  52. okp, err := nkeys.FromSeed([]byte(operatorSeed))
  53. if err != nil {
  54. return "", fmt.Errorf("unable to create operator key pair from seed: %w", err)
  55. }
  56. // Sign the account claims and convert it into a JWT string
  57. ajwt, err := ac.Encode(okp)
  58. if err != nil {
  59. return "", fmt.Errorf("unable to sign the claims: %w", err)
  60. }
  61. return ajwt, nil
  62. }
  63. func createUser(accountSeed, userName string) (string, error) {
  64. ukp, err := nkeys.CreateUser()
  65. if err != nil {
  66. return "", fmt.Errorf("unable to create user using nkeys: %w", err)
  67. }
  68. upub, err := ukp.PublicKey()
  69. if err != nil {
  70. return "", fmt.Errorf("unable to retrieve public key: %w", err)
  71. }
  72. uc := jwt.NewUserClaims(upub)
  73. uc.Name = userName
  74. // Load account key pair
  75. akp, err := nkeys.FromSeed([]byte(accountSeed))
  76. if err != nil {
  77. return "", fmt.Errorf("unable to create account key pair from seed: %w", err)
  78. }
  79. // Sign the user claims and convert it into a JWT string
  80. ujwt, err := uc.Encode(akp)
  81. if err != nil {
  82. return "", fmt.Errorf("unable to sign the claims: %w", err)
  83. }
  84. return ujwt, nil
  85. }

Notes

You can see the key (and any signing keys) of your operator using nsc list keys --show-seeds, you should use a ‘signing key’ to create the account JWTs (as singing keys can be revoked/rotated easily)

To delete accounts use the "$SYS.REQ.CLAIMS.DELETE" (see reference) and make sure to enable JWT deletion in your nats-server resolver (config allow_delete: true in the resolver stanza of the server configuration).

The system is just like any other account, the only difference is that it is listed as system account in the operator’s JWT (and the server config).