Skip to content

Commit

Permalink
[ADDED] Allowed connection type "in process" (#5395)
Browse files Browse the repository at this point in the history
With the JWT library update, there is now a
`jwt.ConnectionTypeInProcess` connection type that can be added to the
list of allowed connection types when defining an user. If the list is
not empty and does not have this type, an "in process" connection will
not be allowed.

Conversely, it is now possible to limit a user to be used only for in
process connections (or several types based on the content of the list).

Possible solution for #5387

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
  • Loading branch information
derekcollison committed May 9, 2024
2 parents e7d3a90 + 3470884 commit 006593f
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 7 deletions.
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -6,7 +6,7 @@ require (
github.com/google/go-tpm v0.9.0
github.com/klauspost/compress v1.17.8
github.com/minio/highwayhash v1.0.2
github.com/nats-io/jwt/v2 v2.5.6
github.com/nats-io/jwt/v2 v2.5.7
github.com/nats-io/nats.go v1.34.1
github.com/nats-io/nkeys v0.4.7
github.com/nats-io/nuid v1.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -5,8 +5,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
github.com/nats-io/jwt/v2 v2.5.6 h1:Cp618+z4q042sWqHiSoIHFT08OZtAskui0hTmRfmGGQ=
github.com/nats-io/jwt/v2 v2.5.6/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
github.com/nats-io/jwt/v2 v2.5.7 h1:j5lH1fUXCnJnY8SsQeB/a/z9Azgu2bYIDvtPVNdxe2c=
github.com/nats-io/jwt/v2 v2.5.7/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
Expand Down
3 changes: 2 additions & 1 deletion server/auth.go
Expand Up @@ -1464,7 +1464,8 @@ func validateAllowedConnectionTypes(m map[string]struct{}) error {
switch ctuc {
case jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket,
jwt.ConnectionTypeLeafnode, jwt.ConnectionTypeLeafnodeWS,
jwt.ConnectionTypeMqtt, jwt.ConnectionTypeMqttWS:
jwt.ConnectionTypeMqtt, jwt.ConnectionTypeMqttWS,
jwt.ConnectionTypeInProcess:
default:
return fmt.Errorf("unknown connection type %q", ct)
}
Expand Down
10 changes: 8 additions & 2 deletions server/client.go
Expand Up @@ -280,6 +280,7 @@ type client struct {
trace bool
echo bool
noIcb bool
iproc bool // In-Process connection, set at creation and immutable.

tags jwt.TagList
nameTag string
Expand Down Expand Up @@ -5958,7 +5959,8 @@ func convertAllowedConnectionTypes(cts []string) (map[string]struct{}, error) {
switch i {
case jwt.ConnectionTypeStandard, jwt.ConnectionTypeWebsocket,
jwt.ConnectionTypeLeafnode, jwt.ConnectionTypeLeafnodeWS,
jwt.ConnectionTypeMqtt, jwt.ConnectionTypeMqttWS:
jwt.ConnectionTypeMqtt, jwt.ConnectionTypeMqttWS,
jwt.ConnectionTypeInProcess:
m[i] = struct{}{}
default:
unknown = append(unknown, i)
Expand All @@ -5985,7 +5987,11 @@ func (c *client) connectionTypeAllowed(acts map[string]struct{}) bool {
case CLIENT:
switch c.clientType() {
case NATS:
want = jwt.ConnectionTypeStandard
if c.iproc {
want = jwt.ConnectionTypeInProcess
} else {
want = jwt.ConnectionTypeStandard
}
case WS:
want = jwt.ConnectionTypeWebsocket
case MQTT:
Expand Down
93 changes: 93 additions & 0 deletions server/client_test.go
Expand Up @@ -2962,3 +2962,96 @@ func TestRemoveHeaderIfPrefixPresent(t *testing.T) {
t.Fatalf("Expected headers to be stripped, got %q", hdr)
}
}

func TestInProcessAllowedConnectionType(t *testing.T) {
tmpl := `
listen: "127.0.0.1:-1"
accounts {
A { users: [{user: "test", password: "pwd", allowed_connection_types: ["%s"]}] }
}
write_deadline: "500ms"
`
for _, test := range []struct {
name string
ct string
inProcessOnly bool
}{
{"conf inprocess", jwt.ConnectionTypeInProcess, true},
{"conf standard", jwt.ConnectionTypeStandard, false},
} {
t.Run(test.name, func(t *testing.T) {
conf := createConfFile(t, []byte(fmt.Sprintf(tmpl, test.ct)))
s, _ := RunServerWithConfig(conf)
defer s.Shutdown()

// Create standard connection
nc, err := nats.Connect(s.ClientURL(), nats.UserInfo("test", "pwd"))
if test.inProcessOnly && err == nil {
nc.Close()
t.Fatal("Expected standard connection to fail, it did not")
}
// Works if nc is nil (which it will if only in-process are allowed)
nc.Close()

// Create inProcess connection
nc, err = nats.Connect(_EMPTY_, nats.UserInfo("test", "pwd"), nats.InProcessServer(s))
if !test.inProcessOnly && err == nil {
nc.Close()
t.Fatal("Expected in-process connection to fail, it did not")
}
// Works if nc is nil (which it will if only standard are allowed)
nc.Close()
})
}
for _, test := range []struct {
name string
ct string
inProcessOnly bool
}{
{"jwt inprocess", jwt.ConnectionTypeInProcess, true},
{"jwt standard", jwt.ConnectionTypeStandard, false},
} {
t.Run(test.name, func(t *testing.T) {
skp, _ := nkeys.FromSeed(oSeed)
spub, _ := skp.PublicKey()

o := defaultServerOptions
o.TrustedKeys = []string{spub}
o.WriteDeadline = 500 * time.Millisecond
s := RunServer(&o)
defer s.Shutdown()

buildMemAccResolver(s)

kp, _ := nkeys.CreateAccount()
aPub, _ := kp.PublicKey()
claim := jwt.NewAccountClaims(aPub)
aJwt, err := claim.Encode(oKp)
require_NoError(t, err)

addAccountToMemResolver(s, aPub, aJwt)

creds := createUserWithLimit(t, kp, time.Time{},
func(j *jwt.UserPermissionLimits) {
j.AllowedConnectionTypes.Add(test.ct)
})
// Create standard connection
nc, err := nats.Connect(s.ClientURL(), nats.UserCredentials(creds))
if test.inProcessOnly && err == nil {
nc.Close()
t.Fatal("Expected standard connection to fail, it did not")
}
// Works if nc is nil (which it will if only in-process are allowed)
nc.Close()

// Create inProcess connection
nc, err = nats.Connect(_EMPTY_, nats.UserCredentials(creds), nats.InProcessServer(s))
if !test.inProcessOnly && err == nil {
nc.Close()
t.Fatal("Expected in-process connection to fail, it did not")
}
// Works if nc is nil (which it will if only standard are allowed)
nc.Close()
})
}
}
11 changes: 10 additions & 1 deletion server/server.go
Expand Up @@ -3091,7 +3091,16 @@ func (s *Server) createClientEx(conn net.Conn, inProcess bool) *client {
}
now := time.Now()

c := &client{srv: s, nc: conn, opts: defaultOpts, mpay: maxPay, msubs: maxSubs, start: now, last: now}
c := &client{
srv: s,
nc: conn,
opts: defaultOpts,
mpay: maxPay,
msubs: maxSubs,
start: now,
last: now,
iproc: inProcess,
}

c.registerWithAccount(s.globalAccount())

Expand Down

0 comments on commit 006593f

Please sign in to comment.