47 Commits

Author SHA1 Message Date
Neil Twigg
7124dc7bdc Revert changes to nbPoolPut, force compressor to forget byte buffer
Signed-off-by: Neil Twigg <neil@nats.io>
2023-10-04 17:41:36 +01:00
Neil Twigg
e20ca9043f Don't append empty slices in the unfragmented path
Signed-off-by: Neil Twigg <neil@nats.io>
2023-10-04 17:18:47 +01:00
Neil Twigg
6b65452bc7 Reduce allocations in WebSocket compression
Signed-off-by: Neil Twigg <neil@nats.io>
2023-10-04 12:36:32 +01:00
Derek Collison
2f1a384bcb Holding onto the compressor and not recycling the interbal byte slice was causing havoc with GC.
This needs to be improved but this at least should allow the GC to cleanup more effectively.

Signed-off-by: Derek Collison <derek@nats.io>
2023-10-03 14:39:00 -07:00
Derek Collison
c5b98f5c79 Make server shutdown an atomic and check inside unsubscribe to avoid unnecessary work.
Signed-off-by: Derek Collison <derek@nats.io>
2023-09-26 17:53:58 -07:00
Derek Collison
7ce47fd182 Move server running state to atomic to avoid contention at NRG layer.
Signed-off-by: Derek Collison <derek@nats.io>
2023-09-25 11:18:15 -07:00
Todd Beets
75d2ddb26b AuthCallout request should include TLS data when client is NATS WS client 2023-09-15 12:36:34 -07:00
Neil Twigg
1527000d1f Use crypto/rand.Read instead of math/rand.Read
As of Go 1.20, `math/rand.Read` is deprecated. In addition to that, it also
isn't recommended for use in combination with anything cryptographic.

I haven't replaced all `math/rand` with `crypto/rand` imports because there
are still some legitimate uses for the `math/rand` package in some places.

Signed-off-by: Neil Twigg <neil@nats.io>
2023-07-13 12:04:58 +01:00
Derek Collison
57d06abbc9 Swap out flate from std for faster one
Signed-off-by: Derek Collison <derek@nats.io>
2023-04-21 12:12:16 -07:00
Neil Twigg
2ece00b08f Buffer re-use in WebSocket code, fix race conditions
Signed-off-by: Neil Twigg <neil@nats.io>
2023-04-21 15:33:48 +01:00
Neil Twigg
f2bffec366 Refactor outbound queues, remove dynamic sizing, add buffer reuse
Also try to reduce flakiness of `TestClusterQueueSubs` and `TestCrossAccountServiceResponseTypes`
2023-03-15 09:37:40 +00:00
Ivan Kozlovic
3c9a7cc6e5 Move to Go 1.19, remote io/util, fix data race and a flapper
Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2022-08-05 09:55:37 -06:00
Ivan Kozlovic
2e07c3f614 [ADDED] MQTT: Support for Websocket
Clients will need to connect to the Websocket port and have `/mqtt`
as the URL path.

Resolves #2433

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-12-06 16:13:13 -07:00
Ivan Kozlovic
833f823efb [IMPROVED] Websocket: added client IP from X-Forwarded-For header
This is for cases when there is a proxy (Nginx, HAProxy, etc..)
between the client and the NATS Server. If this header is present,
the first IP is the one of the originating client and will be
used as the host/IP in server's representation of the client host.

Resolves #2514

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-12-06 15:00:22 -07:00
Ivan Kozlovic
40c0f03153 [FIXED] Monitoring: tls configuration not updated on reload
When creating the http server, we need to provide a TLS configuration.
After a config reload, the new TLS config would not be reflected.

We had the same issue with Websocket and was fixed with the use
of tls.Config.GetConfigForClient API, which makes the TLS handshake
to ask for a TLS config. That fix for websocket was simply not applied
to the HTTPs monitoring case.

I have also fixed some flappers due to the use of localhost instead
of 127.0.0.1 (connections possibly would resolve to some IPv6 address
that the server would not accept, etc..)

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-11-30 10:18:46 -07:00
Ivan Kozlovic
dbfff14d3b [IMPROVED] Websocket: Add client IP in websocket upgrade failures
The error message would now look like this:
```
[8672] 2021/11/01 10:56:50.251985 [ERR] [::1]:59279 - websocket handshake error: invalid value for header 'Upgrade'
```

(without this change the part `[::1]:59279 - ` would not be present)

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-11-01 10:54:22 -06:00
Ivan Kozlovic
9c2c1c61c2 Updates based on code review
- Updated the detection of Safari browser
- Replaced "" with _EMPTY_

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-10-12 11:17:58 -06:00
Ivan Kozlovic
5f2524781c [FIXED] Websocket: issue with compression and Safari
- First issue was it seems that with Safari, we need to use
flate.Writer.Flush() as opposed to Close()
- Then, fragmentation of compressed frames are not processed
correctly by Safari (all other browsers and independant websocket
libraries reassemble them well). So disable compression + fragmentation
for Safari browsers.
- Added a compression threshold so we don't compress small buffers.

Resolves #2612

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-10-11 17:28:40 -06:00
Ivan Kozlovic
8d41daf6ab Updates based on code review
Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-09-13 14:06:17 -06:00
Ivan Kozlovic
1727c6061c [FIXED] Websocket: possible panic when decoding CLOSE frame
Resolves #2510

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-09-12 15:35:49 -06:00
Ivan Kozlovic
d7a124baaf [FIXED] LeafNode with "wss://.." url was not always initiating TLS
If the remote did not have any TLS configuration, the URL scheme
"wss://" was not used as the indicating that the connection should
be attempted as a TLS connection, causing "invalid websocket connection"
in the server attempting to create the remote leafnode connection.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-08-15 12:39:10 -06:00
Ivan Kozlovic
189336417f [FIXED] Websocket compression/decompression issue with continuation frames
For compression, continuation frames had the compress bit set, which is
wrong since only the first frame should.
For decompression, continuation frames were decompressed individually
instead of assembling the full payload and then decompressing.

Resolves #2287

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-06-21 11:09:19 -06:00
Matthias Hanel
6f6f22e9a7 [added] pinned_cert option to tls block hex(sha256(spki)) (#2233)
* [added] pinned_cert option to tls block hex(sha256(spki))

When read form config, the values are automatically lower cased.
The check when seeing the values programmatically requires 
lower case to avoid having to alter the map at this point.

Signed-off-by: Matthias Hanel <mh@synadia.com>
2021-05-20 17:00:09 -04:00
Ivan Kozlovic
854cc15ddb Fixed typos and updates checkOrigin() doc
Also fixed two flappers

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-05-11 19:36:46 -06:00
Ivan Kozlovic
da91b06218 [FIXED] Websocket: Origin should be checked only if present
There are 2 options, same_origin and allowed_origins that should
apply only to webbrowsers that set the Origin http header. If
the header is not present, the server should not fail direct
clients using websocket protocol, or leafnodes.

From spec:
https://datatracker.ietf.org/doc/html/rfc6455#section-1.6

   The WebSocket Protocol uses the origin model used by web browsers to
   restrict which web pages can contact a WebSocket server when the
   WebSocket Protocol is used from a web page.  Naturally, when the
   WebSocket Protocol is used by a dedicated client directly (i.e., not
   from a web page through a web browser), the origin model is not
   useful, as the client can provide any arbitrary origin string.

Resolves #2207

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-05-11 18:37:16 -06:00
Jaime Piña
e12181cb83 Return not ready for connection reason
Currently, we use ReadyForConnections in server tests to wait for the
server to be ready. However, when this fails we don't get a clue about
why it failed.

This change adds a new unexported method called readyForConnections that
returns an error describing which check failed. The exported
ReadyForConnections version works exactly as before. The unexported
version gets used in internal tests only.
2021-04-20 11:45:08 -07:00
Ivan Kozlovic
f96f36c73d [FIXED] Websocket: TLS configuration not updated on reload
Although we do not support websocket configuration changes, we usually
try to support changes to TLS certificates, etc..

The way websocket is handled (using an http server), the TLS config
was given on startup and updates following a configuration reload
would not be reflected.
Using a tls.Config function that allows passing the tls config
prior to handshake seem to workaround this issue.

I have added a test that demonstrate that the TLS configuration
is really updated after the reload.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-04-07 13:58:35 -06:00
Ivan Kozlovic
9c036778c0 [FIXED] Websocket: print correct random port instead of 0
When a random port is selected (likely in tests), the banner would
print the port as `0` instead of its real value.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-03-29 16:01:38 -06:00
Ivan Kozlovic
b17f38e356 [FIXED] Websocket: do not generate empty frames + LN corruption
- It was possible that when the server was sending frames to a
webbrowser, it would send empty frames. While technically not wrong,
prevent that from happening.
- Not copying enqueued buffers could cause corruption with LN+WS.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-03-26 16:17:46 -06:00
Matthias Hanel
c50ee2a1c6 [Changed] all times exposed will be computed in UTC (#1943)
This also applies to times that end up in that json.
Where applicable moved time.Now() to where it is used.
Moved calls to .UTC() to where time is created it that time is converted
later anyway.

Signed-off-by: Matthias Hanel <mh@synadia.com>
2021-03-02 21:37:42 -05:00
Ivan Kozlovic
ac0a1ee8fd Fixed compression http header request/response
The issue was introduced by PR #1858.

Key points:

- Sec-WebSocket-Extensions must contain approved headers, so moving
the "no-masking" private extension to its own header "Nats-No-Masking".

- The format of the permessage-deflate negotiation response became
invalid, I have fixed that.

- For leaf nodes, if `permessage-deflate` extension is not at all
present in the response, then simply disable compression, however
if it is present but there is no server/client no context take over,
then we have to fail the connection.

- A leafnode test was not setting the "NoMasking" option so the
test TestLeafNodeWSNoMaskingRejected was not capturing possible
error if negotiation failed.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-02-01 12:10:37 -07:00
Ivan Kozlovic
2b8c6e0124 Support for Websocket Leafnode connections
Added two options in the remote leaf node configuration

- compress, for websocket only at the moment
- ws_masking, to force remote leafnode connections to mask websocket
frames (default is no masking since it is communication between
server to server)

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2021-01-28 13:13:11 -07:00
Ivan Kozlovic
67425d23c8 Add c.isMqtt() and c.isWebsocket()
This hides the check on "c.mqtt != nil" or "c.ws != nil".
Added some tests.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-12-02 15:52:06 -07:00
Ivan Kozlovic
41fac39f8e Split createClient() into versions for normal, WS and MQTT clients.
This duplicate quite a bit of code, but reduces the conditionals
in the createClient() function.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-12-02 13:50:50 -07:00
Ivan Kozlovic
1dba6418ed [ADDED] MQTT Support
This PR introduces native support for MQTT clients. It requires use
of accounts with JetStream enabled. Since as of now clustering is
not available, MQTT will be limited to single instance.

Only QoS 0 and 1 are supported at the moment. MQTT clients can
exchange messages with NATS clients and vice-versa.

Since JetStream is required, accounts with JetStream enabled must
exist in order for an MQTT client to connect to the NATS Server.
The administrator can limit the users that can use MQTT with the
allowed_connection_types option in the user section. For instance:
```
accounts {
  mqtt {
    users [
      {user: all, password: pwd, allowed_connection_types: ["STANDARD", "WEBSOCKET", "MQTT"]}
      {user: mqtt_only, password: pwd, allowed_connection_types: "MQTT"}
    ]
    jetstream: enabled
  }
}
```
The "mqtt_only" can only be used for MQTT connections, which the user
"all" accepts standard, websocket and MQTT clients.

Here is what a configuration to enable MQTT looks like:
```
mqtt {
  # Specify a host and port to listen for websocket connections
  #
  # listen: "host:port"

  # It can also be configured with individual parameters,
  # namely host and port.
  #
  # host: "hostname"
  port: 1883

  # TLS configuration section
  #
  # tls {
  #  cert_file: "/path/to/cert.pem"
  #  key_file: "/path/to/key.pem"
  #  ca_file: "/path/to/ca.pem"
  #
  #  # Time allowed for the TLS handshake to complete
  #  timeout: 2.0
  #
  #  # Takes the user name from the certificate
  #  #
  #  # verify_an_map: true
  #}

  # Authentication override. Here are possible options.
  #
  # authorization {
  #   # Simple username/password
  #   #
  #   user: "some_user_name"
  #   password: "some_password"
  #
  #   # Token. The server will check the MQTT's password in the connect
  #   # protocol against this token.
  #   #
  #   # token: "some_token"
  #
  #   # Time allowed for the client to send the MQTT connect protocol
  #   # after the TCP connection is established.
  #   #
  #   timeout: 2.0
  #}

  # If an MQTT client connects and does not provide a username/password and
  # this option is set, the server will use this client (and therefore account).
  #
  # no_auth_user: "some_user_name"

  # This is the time after which the server will redeliver a QoS 1 message
  # sent to a subscription that has not acknowledged (PUBACK) the message.
  # The default is 30 seconds.
  #
  # ack_wait: "1m"

  # This limits the number of QoS1 messages sent to a session without receiving
  # acknowledgement (PUBACK) from that session. MQTT specification defines
  # a packet identifier as an unsigned int 16, which means that the maximum
  # value is 65535. The default value is 1024.
  #
  # max_ack_pending: 100
}
```

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-11-30 20:08:44 -07:00
Waldemar Quevedo
33a9391519 Fix typo in websocket err message
Signed-off-by: Waldemar Quevedo <wally@synadia.com>
2020-10-15 13:42:22 -07:00
Ivan Kozlovic
7ccbaca782 Added an allowed connection type filter for users
Users and NKey users will now have the option to specify a list
of allowed connection types.

This will allow for instance a certain user to be allowed to
connect as a standard NATS client, but not as Websocket, or
vice-versa.

This also fixes the websocket auth override. Indeed, with
the original behavior, the websocket users would have been bound
to $G, which would not work when there are accounts defined, since
when that is the case, no app can connect/bind to $G account.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-09-16 18:22:44 -06:00
Ivan Kozlovic
20a67a5be8 Websocket: add option to disable TLS
The new option Websocket.NoTLS would have to be set to true
to disable the server check that enforces TLS configuration.

Resolves #1529

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-07-29 17:33:02 -06:00
Ivan Kozlovic
b9764db478 Renamed gossipURLs type and moved its declaration to util.go
Also made the add/remove/getAsStringSlice receiver for this type.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-07-16 11:22:58 -06:00
Ivan Kozlovic
9b0967a5d1 [FIXED] Handling of gossiped URLs
If some servers in the cluster have the same connect URLs (due
to the use of client advertise), then it would be possible to
have a server sends the connect_urls INFO update to clients with
missing URLs.

Resolves #1515

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-07-15 17:39:12 -06:00
Ivan Kozlovic
9288283d90 Fixed accept loops that could leave connections opened
This was discovered with the test TestLeafNodeWithGatewaysServerRestart
that was sometimes failing. Investigation showed that when cluster B
was shutdown, one of the server on A that had a connection from B
that just broke tried to reconnect (as part of reconnect retries of
implicit gateways) to a server in B that was in the process of shuting down.
The connection had been accepted but createGateway not called because
the server's running boolean had been set to false as part of the shutdown.
However, the connection was not closed so the server on A had a valid
connection to a dead server from cluster B. When the B cluster (now single
server) was restarted and a LeafNode connection connected to it, then
the gateway from B to A was created, that server on A did not create outbound
connection to that B server because it already had one (the zombie one).

So this PR strengthens the starting of accept loops and also make sure
that if a connection (all type of connections) is not accepted because
the server is shuting down, that connection is properly closed.

Since all accept loops had almost same code, made a generic function
that accept functions to call specific create connection functions.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-07-06 17:03:19 -06:00
Pavel Khlebovich
d5eb9ff6f2 Support Cookie JWT auth via WebSocket 2020-06-18 19:27:42 +03:00
Ivan Kozlovic
d2a8282a0d [FIXED] LeafNode TLSMap and websocket auth override
We added authentication override block for websocket configuration
in PR #1463 and #1465 which somehow introduced a drop in perf as
reported by the bench tests.
This PR refactors a bit to restore the performance numbers.

This change also fixes the override behavior for websocket auth:
- If websocket's NoAuthUser is configured, the websocket's auth
  block MUST define Users, and the user be present.
- If there is any override (username/pwd,token,etc..) then the
  whole block config will be used when authenticating a websocket
  client, which means that if websocket NoAuthUser is empty we
  are not falling back to the regular client's NoAuthUser config.
- TLSMap always override the regular client's config. That is,
  whatever TLSMap value specified in the websocket's tls{} block
  will be used.

The TLSMap configuration was not used for LeafNodes. The behavior
now will be:
- If LeafNode's auth block contains users and TLSMap is true,
  the user is looked up based on the cert's info. If not found,
  authentication will fail. If found, it will be authenticated
  and bound to associated account.
- If no user is specified in LeafNode's auth block and TLSMap
  is true, then the cert's info will be used against the global
  users map.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-06-11 17:06:54 -06:00
Ivan Kozlovic
313cf898c1 Refactor some code to make it reuseable
Building server's nkeys and users map out of slices form options
has been made a function so it can be used for the server and
websocket (and in future for mqtt)

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-06-09 17:27:51 -06:00
Ivan Kozlovic
01b14c2abe Added dedicated auth block for websocket
Websocket can now override
- Username/password
- Token
- Users
- NKeys
- no_auth_user
- auth_timeout

For TLS, support for verify and verify_and_map. We used to set
tls config's ClientAuth to NoClientCert. It will now depend
if the config requires client certificate verification, which
is needed if TLSMap is enabled.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-06-09 11:29:52 -06:00
Ivan Kozlovic
98ea70a590 LameDuckMode takes into account websocket accept loop
This is related to #1408.
Make sure that we close the websocket "accept loop" if configured
before proceeding with the lame duck mode.

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-06-02 17:49:38 -06:00
Ivan Kozlovic
9715848a8e [ADDED] Websocket support
Websocket support can be enabled with a new websocket
configuration block:

```
websocket {
    # Specify a host and port to listen for websocket connections
    # listen: "host:port"

    # It can also be configured with individual parameters,
    # namely host and port.
    # host: "hostname"
    # port: 4443

    # This will optionally specify what host:port for websocket
    # connections to be advertised in the cluster
    # advertise: "host:port"

    # TLS configuration is required
    tls {
      cert_file: "/path/to/cert.pem"
      key_file: "/path/to/key.pem"
    }

    # If same_origin is true, then the Origin header of the
    # client request must match the request's Host.
    # same_origin: true

    # This list specifies the only accepted values for
    # the client's request Origin header. The scheme,
    # host and port must match. By convention, the
    # absence of port for an http:// scheme will be 80,
    # and for https:// will be 443.
    # allowed_origins [
    #    "http://www.example.com"
    #    "https://www.other-example.com"
    # ]

    # This enables support for compressed websocket frames
    # in the server. For compression to be used, both server
    # and client have to support it.
    # compression: true

    # This is the total time allowed for the server to
    # read the client request and write the response back
    # to the client. This include the time needed for the
    # TLS handshake.
    # handshake_timeout: "2s"
}
```

Signed-off-by: Ivan Kozlovic <ivan@synadia.com>
2020-05-20 11:14:39 -06:00