Add a no_auth_user

This configuration allows to refer to a configured user to be used when
the connection provides no credentials.

Signed-off-by: Matthias Hanel <mh@synadia.com>
This commit is contained in:
Matthias Hanel
2020-04-27 13:47:03 -04:00
parent a8be338bc1
commit b074c941ae
4 changed files with 110 additions and 7 deletions

View File

@@ -16,6 +16,7 @@ package server
import (
"crypto/tls"
"encoding/base64"
"fmt"
"net"
"strings"
"time"
@@ -383,11 +384,19 @@ func (s *Server) processClientOrLeafAuthentication(c *client) bool {
// Already checked that the client didn't send a user in connect
// but we set it here to be able to identify it in the logs.
c.opts.Username = euser
} else if c.opts.Username != "" {
user, ok = s.users[c.opts.Username]
if !ok {
s.mu.Unlock()
return false
} else {
if c.kind == CLIENT && c.opts.Username == "" && s.opts.NoAuthUser != "" {
if u, exists := s.users[s.opts.NoAuthUser]; exists {
c.opts.Username = u.Username
c.opts.Password = u.Password
}
}
if c.opts.Username != "" {
user, ok = s.users[c.opts.Username]
if !ok {
s.mu.Unlock()
return false
}
}
}
}
@@ -693,3 +702,23 @@ func comparePasswords(serverPassword, clientPassword string) bool {
}
return true
}
func validateAuth(o *Options) error {
if o.NoAuthUser == "" {
return nil
}
if len(o.TrustedOperators) > 0 {
return fmt.Errorf("no_auth_user not compatible with Trusted Operator")
}
if o.Users == nil {
return fmt.Errorf(`no_auth_user: "%s" present, but users are not defined`, o.NoAuthUser)
}
for _, u := range o.Users {
if u.Username == o.NoAuthUser {
return nil
}
}
return fmt.Errorf(
`no_auth_user: "%s" not present as user in authorization block or account configuration`,
o.NoAuthUser)
}

View File

@@ -163,6 +163,7 @@ type Options struct {
Nkeys []*NkeyUser `json:"-"`
Users []*User `json:"-"`
Accounts []*Account `json:"-"`
NoAuthUser string `json:"-"`
SystemAccount string `json:"-"`
AllowNewAccounts bool `json:"-"`
Username string `json:"-"`
@@ -783,6 +784,8 @@ func (o *Options) processConfigFileLine(k string, v interface{}, errors *[]error
o.resolverPreloads[key] = jwtstr
}
}
case "no_auth_user":
o.NoAuthUser = v.(string)
case "system_account", "system":
// Already processed at the beginning so we just skip them
// to not treat them as unknown values.

View File

@@ -26,6 +26,8 @@ import (
"strings"
"testing"
"time"
"github.com/nats-io/nats.go"
)
func checkOptionsEqual(t *testing.T, golden, opts *Options) {
@@ -2460,3 +2462,70 @@ func TestExpandPath(t *testing.T) {
})
}
}
func TestNoAuthUserCode(t *testing.T) {
confFileName := createConfFile(t, []byte(`
listen: "127.0.0.1:-1"
no_auth_user: $NO_AUTH_USER
accounts {
synadia {
users [
{user: "a", password: "a"},
{nkey : UBAAQWTW6CG2G6ANGNKB5U2B7HRWHSGMZEZX3AQSAJOQDAUGJD46LD2E},
]
}
acc {
users [
{user: "c", password: "c"}
]
}
}
# config for $G
authorization {
users [
{user: "b", password: "b"}
]
}
`))
defer os.Remove(confFileName)
defer os.Unsetenv("NO_AUTH_USER")
for _, user := range []string{"a", "b", "b"} {
t.Run(user, func(t *testing.T) {
os.Setenv("NO_AUTH_USER", user)
opts, err := ProcessConfigFile(confFileName)
if err != nil {
t.Fatalf("Received unexpected error %s", err)
} else {
srv := RunServer(opts)
nc, err := nats.Connect(fmt.Sprintf("nats://127.0.0.1:%d", opts.Port))
if err != nil {
t.Fatalf("couldn't connect %s", err)
}
nc.Close()
srv.Shutdown()
}
})
}
for _, badUser := range []string{"notthere", "UBAAQWTW6CG2G6ANGNKB5U2B7HRWHSGMZEZX3AQSAJOQDAUGJD46LD2E"} {
t.Run(badUser, func(t *testing.T) {
os.Setenv("NO_AUTH_USER", badUser)
opts, err := ProcessConfigFile(confFileName)
if err != nil {
t.Fatalf("Received unexpected error %s", err)
}
s, err := NewServer(opts)
if err != nil {
if !strings.HasPrefix(err.Error(), "no_auth_user") {
t.Fatalf("Received unexpected error %s", err)
}
return // error looks as expected
}
s.Shutdown()
t.Fatalf("Received no error, where no_auth_user error was expected")
})
}
}

View File

@@ -410,8 +410,10 @@ func validateOptions(o *Options) error {
if err := validateLeafNode(o); err != nil {
return err
}
// Check that gateway is properly configured. Returns no error
// if there is no gateway defined.
// Check that authentication is properly configured.
if err := validateAuth(o); err != nil {
return err
}
return validateGatewayOptions(o)
}