mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Added update to parse and load operator JWTs. Changed to add in signing keys from operator JWT to list of trusted keys. Added URL account resolver. Added account claim updates by system messages. Signed-off-by: Derek Collison <derek@nats.io>
236 lines
7.0 KiB
Go
236 lines
7.0 KiB
Go
// Copyright 2018 The NATS Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/nats-io/gnatsd/server"
|
|
"github.com/nats-io/go-nats"
|
|
"github.com/nats-io/jwt"
|
|
"github.com/nats-io/nkeys"
|
|
)
|
|
|
|
const testOpConfig = "./configs/operator.conf"
|
|
|
|
// This matches ./configs/nkeys_jwts/test.seed
|
|
// Test operator seed.
|
|
var oSeed = []byte("SOAFYNORQLQFJYBYNUGC5D7SH2MXMUX5BFEWWGHN3EK4VGG5TPT5DZP7QU")
|
|
|
|
// This is a signing key seed.
|
|
var skSeed = []byte("SOAEL3NFOTU6YK3DBTEKQYZ2C5IWSVZWWZCQDASBUOHJKBFLVANK27JMMQ")
|
|
|
|
func checkKeys(t *testing.T, opts *server.Options, opc *jwt.OperatorClaims, expected int) {
|
|
// We should have filled in the TrustedKeys here.
|
|
if len(opts.TrustedKeys) != expected {
|
|
t.Fatalf("Should have %d trusted keys, got %d", expected, len(opts.TrustedKeys))
|
|
}
|
|
// Check that we properly placed all keys from the opc into TrustedKeys
|
|
chkMember := func(s string) {
|
|
for _, c := range opts.TrustedKeys {
|
|
if s == c {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("Expected %q to be in TrustedKeys", s)
|
|
}
|
|
chkMember(opc.Issuer)
|
|
for _, sk := range opc.SigningKeys {
|
|
chkMember(sk)
|
|
}
|
|
}
|
|
|
|
// This will test that we enforce certain restrictions when you use trusted operators.
|
|
// Like auth is always true, can't define accounts or users, required to define an account resolver, etc.
|
|
func TestOperatorRestrictions(t *testing.T) {
|
|
opts, err := server.ProcessConfigFile(testOpConfig)
|
|
if err != nil {
|
|
t.Fatalf("Error processing config file: %v", err)
|
|
}
|
|
if _, err := server.NewServer(opts); err != nil {
|
|
t.Fatalf("Expected to create a server successfully")
|
|
}
|
|
// TrustedKeys get defined when processing from above, trying again with
|
|
// same opts should not work.
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error with TrustedKeys defined")
|
|
}
|
|
// Must wipe and rebuild to succeed.
|
|
wipeOpts := func() {
|
|
opts.TrustedKeys = nil
|
|
opts.Accounts = nil
|
|
opts.Users = nil
|
|
opts.Nkeys = nil
|
|
opts.AllowNewAccounts = false
|
|
}
|
|
|
|
wipeOpts()
|
|
opts.Accounts = []*server.Account{&server.Account{Name: "TEST"}}
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error with Accounts defined")
|
|
}
|
|
wipeOpts()
|
|
opts.Users = []*server.User{&server.User{Username: "TEST"}}
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error with Users defined")
|
|
}
|
|
wipeOpts()
|
|
opts.Nkeys = []*server.NkeyUser{&server.NkeyUser{Nkey: "TEST"}}
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error with Nkey Users defined")
|
|
}
|
|
wipeOpts()
|
|
opts.AllowNewAccounts = true
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error with AllowNewAccounts set to true")
|
|
}
|
|
|
|
wipeOpts()
|
|
opts.AccountResolver = nil
|
|
if _, err := server.NewServer(opts); err == nil {
|
|
t.Fatalf("Expected an error without an AccountResolver defined")
|
|
}
|
|
}
|
|
|
|
func TestOperatorConfig(t *testing.T) {
|
|
opts, err := server.ProcessConfigFile(testOpConfig)
|
|
if err != nil {
|
|
t.Fatalf("Error processing config file: %v", err)
|
|
}
|
|
// Check we have the TrustedOperators
|
|
if len(opts.TrustedOperators) != 1 {
|
|
t.Fatalf("Expected to load the operator")
|
|
}
|
|
_, err = server.NewServer(opts)
|
|
if err != nil {
|
|
t.Fatalf("Expected to create a server: %v", err)
|
|
}
|
|
// We should have filled in the TrustedKeys here.
|
|
// Our master key (issuer) plus the signing keys (3).
|
|
checkKeys(t, opts, opts.TrustedOperators[0], 4)
|
|
}
|
|
|
|
func runOperatorServer(t *testing.T) (*server.Server, *server.Options) {
|
|
return RunServerWithConfig(testOpConfig)
|
|
}
|
|
|
|
func createAccountForOperatorKey(t *testing.T, s *server.Server, seed []byte) (*server.Account, nkeys.KeyPair) {
|
|
t.Helper()
|
|
okp, _ := nkeys.FromSeed(seed)
|
|
akp, _ := nkeys.CreateAccount()
|
|
pub, _ := akp.PublicKey()
|
|
nac := jwt.NewAccountClaims(pub)
|
|
jwt, _ := nac.Encode(okp)
|
|
if err := s.AccountResolver().Store(pub, jwt); err != nil {
|
|
t.Fatalf("Account Resolver returned an error: %v", err)
|
|
}
|
|
return s.LookupAccount(pub), akp
|
|
}
|
|
|
|
func createAccount(t *testing.T, s *server.Server) (*server.Account, nkeys.KeyPair) {
|
|
t.Helper()
|
|
return createAccountForOperatorKey(t, s, oSeed)
|
|
}
|
|
|
|
func createUserCreds(t *testing.T, s *server.Server, akp nkeys.KeyPair) nats.Option {
|
|
t.Helper()
|
|
kp, _ := nkeys.CreateUser()
|
|
pub, _ := kp.PublicKey()
|
|
nuc := jwt.NewUserClaims(pub)
|
|
ujwt, err := nuc.Encode(akp)
|
|
if err != nil {
|
|
t.Fatalf("Error generating user JWT: %v", err)
|
|
}
|
|
userCB := func() (string, error) {
|
|
return ujwt, nil
|
|
}
|
|
sigCB := func(nonce []byte) ([]byte, error) {
|
|
sig, _ := kp.Sign(nonce)
|
|
return sig, nil
|
|
}
|
|
return nats.UserJWT(userCB, sigCB)
|
|
}
|
|
|
|
func TestOperatorServer(t *testing.T) {
|
|
s, opts := runOperatorServer(t)
|
|
defer s.Shutdown()
|
|
|
|
url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
|
|
if _, err := nats.Connect(url); err == nil {
|
|
t.Fatalf("Expected to fail with no credentials")
|
|
}
|
|
|
|
_, akp := createAccount(t, s)
|
|
nc, err := nats.Connect(url, createUserCreds(t, s, akp))
|
|
if err != nil {
|
|
t.Fatalf("Error on connect: %v", err)
|
|
}
|
|
nc.Close()
|
|
|
|
// Now create an account from another operator, this should fail.
|
|
okp, _ := nkeys.CreateOperator()
|
|
seed, _ := okp.Seed()
|
|
_, akp = createAccountForOperatorKey(t, s, seed)
|
|
_, err = nats.Connect(url, createUserCreds(t, s, akp))
|
|
if err == nil {
|
|
t.Fatalf("Expected error on connect")
|
|
}
|
|
}
|
|
|
|
func TestOperatorSystemAccount(t *testing.T) {
|
|
s, _ := runOperatorServer(t)
|
|
defer s.Shutdown()
|
|
|
|
// Create an account from another operator, this should fail if used as a system account.
|
|
okp, _ := nkeys.CreateOperator()
|
|
seed, _ := okp.Seed()
|
|
acc, _ := createAccountForOperatorKey(t, s, seed)
|
|
if err := s.SetSystemAccount(acc.Name); err == nil {
|
|
t.Fatalf("Expected this to fail")
|
|
}
|
|
if acc := s.SystemAccount(); acc != nil {
|
|
t.Fatalf("Expected no account to be set for system account")
|
|
}
|
|
|
|
acc, _ = createAccount(t, s)
|
|
if err := s.SetSystemAccount(acc.Name); err != nil {
|
|
t.Fatalf("Expected this succeed, got %v", err)
|
|
}
|
|
if sysAcc := s.SystemAccount(); sysAcc != acc {
|
|
t.Fatalf("Did not get matching account for system account")
|
|
}
|
|
}
|
|
|
|
func TestOperatorSigningKeys(t *testing.T) {
|
|
s, opts := runOperatorServer(t)
|
|
defer s.Shutdown()
|
|
|
|
// Create an account with a signing key, not the master key.
|
|
acc, akp := createAccountForOperatorKey(t, s, skSeed)
|
|
|
|
// Make sure we can set system account.
|
|
if err := s.SetSystemAccount(acc.Name); err != nil {
|
|
t.Fatalf("Expected this succeed, got %v", err)
|
|
}
|
|
|
|
// Make sure we can create users with it too.
|
|
url := fmt.Sprintf("nats://%s:%d", opts.Host, opts.Port)
|
|
nc, err := nats.Connect(url, createUserCreds(t, s, akp))
|
|
if err != nil {
|
|
t.Fatalf("Error on connect: %v", err)
|
|
}
|
|
nc.Close()
|
|
}
|