Skip to content

Commit b446ec2

Browse files
author
Prabhakar Kumar
committed
Tightens security of the matlab-proxy when token authentication is enabled, for multi-user shared system environments.
1 parent 4faf4b7 commit b446ec2

File tree

9 files changed

+529
-134
lines changed

9 files changed

+529
-134
lines changed

Advanced-Usage.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ The following table describes all the environment variables that you can set to
2626
| **TMPDIR** or **TMP** | string | `"/path/for/MATLAB/to/use/as/tmp"` | Set either one of these variables to control the temporary folder used by MATLAB. `TMPDIR` takes precedence over `TMP` and if neither variable is set, `/tmp` is the default value used by MATLAB. |
2727
| **MWI_SSL_CERT_FILE** | string | `"/path/to/certificate.pem"` | The certfile string must be the path to a single file in PEM format containing the certificate as well as any number of CA certificates needed to establish the certificate’s authenticity. See [SSL Support](./SECURITY.md#ssl-support) for more information.|
2828
| **MWI_SSL_KEY_FILE** | string | `"/path/to/keyfile.key"` | The keyfile string, if present, must point to a file containing the private key. Otherwise the private key will be taken from certfile as well. |
29-
| **MWI_ENABLE_TOKEN_AUTH** | string | `"True"` | When set to `True`, matlab-proxy will require users to provide the security token to access the proxy. <br />The default value is `False` . See [Token-Based Authentication](./SECURITY.md#token-based-authentication) for more information.|
30-
| **MWI_AUTH_TOKEN** | string (optional) | `"AnyURLSafeToken"` | Optionally, provide a custom `token` for use with `MWI_ENABLE_TOKEN_AUTH`. A token can safely contain any combination of alpha numeric text along with the following permitted characters: `- . _ ~`.<br />When absent matlab-proxy will generate a random URL safe token. |
29+
| **MWI_ENABLE_TOKEN_AUTH** | string | `"True"` | When set to `True`, matlab-proxy will require users to provide the security token to access the proxy. One can optionally set the token using the environment variable `MWI_AUTH_TOKEN`. If `MWI_AUTH_TOKEN` is not specified, then a token will be generated for you. <br />The default value is `False` . See [Token-Based Authentication](./SECURITY.md#token-based-authentication) for more information.|
30+
| **MWI_AUTH_TOKEN** | string (optional) | `"AnyURLSafeToken"` | Specify a custom `token` for matlab-proxy to use with [Token-Based Authentication](./SECURITY.md#token-based-authentication). A token can safely contain any combination of alpha numeric text along with the following permitted characters: `- . _ ~`.<br />When absent matlab-proxy will generate a random URL safe token. |
3131
| **MWI_USE_EXISTING_LICENSE** | string (optional) | `"True"` | When set to True, matlab-proxy will not ask you for additional licensing information and will try to launch an already activated MATLAB on your system PATH.
3232
| **MWI_CUSTOM_MATLAB_ROOT** | string (optional) | `"/path/to/matlab/root/"` | Optionally, provide a custom path to MATLAB root. For more information see [Adding MATLAB to System Path](#adding-matlab-to-system-path) |
3333

SECURITY.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ This token can be provided to the server in 2 ways:
7777
<img width="800" src="./img/token_authentication_page.png">
7878
</p>
7979

80+
3. Through a `mwi_auth_token` header. Example:
81+
``` html
82+
mwi_auth_token:abcdef..
83+
```
84+
8085
**NOTE** : Its highly recommended to use this feature along with SSL enabled as shown [here](#use-token-authentication-with-ssl-enabled).
8186

8287
### **Use with auto-generated tokens**

gui/src/actionCreators/index.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,8 @@ export function updateAuthStatus(token){
220220
const options = {
221221
method: 'POST',
222222
headers: {
223-
'Accept': 'application/text',
224-
'Content-Type': 'application/text'
223+
'mwi_auth_token': token
225224
},
226-
body: token
227225
};
228226
const response = await fetchWithTimeout(dispatch, './authenticate_request', options, 15000);
229227
const data = await response.json()

matlab_proxy/app.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
import matlab_proxy
1616
from matlab_proxy import constants, settings, util
1717
from matlab_proxy.app_state import AppState
18-
from matlab_proxy.default_configuration import config
19-
from matlab_proxy.util import list_servers, mwi
18+
from matlab_proxy.util import mwi
2019
from matlab_proxy.util.mwi import environment_variables as mwi_env
2120
from matlab_proxy.util.mwi import token_auth
2221
from matlab_proxy.util.mwi.exceptions import AppError, InvalidTokenError, LicensingError
@@ -119,6 +118,9 @@ async def create_status_response(app, loadUrl=None):
119118
)
120119

121120

121+
# @token_auth.authenticate_access_decorator
122+
# Explicitly disabling authentication for this end-point,
123+
# because the front end requires this endpoint to be available at all times.
122124
async def get_env_config(req):
123125
"""API Endpoint to get Matlab Web Desktop environment specific configuration.
124126
@@ -142,6 +144,9 @@ async def get_env_config(req):
142144
return web.json_response(config)
143145

144146

147+
# @token_auth.authenticate_access_decorator
148+
# Explicitly disabling authentication for this end-point,
149+
# because the front end requires this endpoint to be available at all times.
145150
async def get_status(req):
146151
"""API Endpoint to get the generic status of the server, MATLAB and MATLAB Licensing.
147152
@@ -154,39 +159,39 @@ async def get_status(req):
154159
return await create_status_response(req.app)
155160

156161

162+
# @token_auth.authenticate_access_decorator
163+
# Explicitly disabling authentication for this end-point, as it checks for authenticity internally.
157164
async def authenticate_request(req):
158165
"""API Endpoint to authenticate request to access server
159166
160167
Returns:
161168
JSONResponse: JSONResponse object containing information about authentication status and error if any.
162169
"""
163-
if await token_auth.authenticate_request(req):
164-
logger.debug("!!!!!! REQUEST IS AUTHORIZED !!!!")
165-
authStatus = True
166-
error = None
167-
else:
168-
logger.debug("!!!!!! REQUEST IS NOT AUTHORIZED !!!!")
169-
authStatus = False
170-
error = marshal_error(
170+
is_authenticated = await token_auth.authenticate_request(req)
171+
error = (
172+
None
173+
if is_authenticated
174+
else marshal_error(
171175
InvalidTokenError(
172176
"Token invalid. Please enter a valid token to authenticate"
173177
)
174178
)
175-
179+
)
176180
# If there is an error, state.error is not updated because the client may have set the
177181
# token incorrectly which is not an error raised on the backend.
178-
179-
token = await req.text() if not error else ""
182+
# TODO: @krisctl to remove this.
183+
token = req.app["settings"]["mwi_auth_token"] if is_authenticated else ""
180184

181185
return web.json_response(
182186
{
183-
"authStatus": authStatus,
187+
"authStatus": is_authenticated,
184188
"authToken": token,
185189
"error": error,
186190
}
187191
)
188192

189193

194+
@token_auth.authenticate_access_decorator
190195
async def start_matlab(req):
191196
"""API Endpoint to start MATLAB
192197
@@ -204,6 +209,7 @@ async def start_matlab(req):
204209
return await create_status_response(req.app)
205210

206211

212+
@token_auth.authenticate_access_decorator
207213
async def stop_matlab(req):
208214
"""API Endpoint to stop MATLAB
209215
@@ -220,6 +226,7 @@ async def stop_matlab(req):
220226
return await create_status_response(req.app)
221227

222228

229+
@token_auth.authenticate_access_decorator
223230
async def set_licensing_info(req):
224231
"""API Endpoint to set licensing information on the server side.
225232
@@ -261,6 +268,7 @@ async def set_licensing_info(req):
261268
return await create_status_response(req.app)
262269

263270

271+
@token_auth.authenticate_access_decorator
264272
async def update_entitlement(req):
265273
"""API endpoint to update selected entitlement to start MATLAB with.
266274
@@ -290,6 +298,7 @@ async def __start_matlab_if_licensed(state):
290298
await state.start_matlab(restart_matlab=True)
291299

292300

301+
@token_auth.authenticate_access_decorator
293302
async def licensing_info_delete(req):
294303
"""API Endpoint to stop MATLAB and remove licensing details.
295304
@@ -312,6 +321,7 @@ async def licensing_info_delete(req):
312321
return await create_status_response(req.app)
313322

314323

324+
@token_auth.authenticate_access_decorator
315325
async def termination_integration_delete(req):
316326
"""API Endpoint to terminate the Integration and shutdown the server.
317327
@@ -339,6 +349,8 @@ async def termination_integration_delete(req):
339349
sys.exit(0)
340350

341351

352+
# @token_auth.authenticate_access_decorator
353+
# Explicitly disabling authentication for this end-point, as authenticity is checked by the redirected endpoint.
342354
async def root_redirect(request):
343355
"""API Endpoint to return the root index.html file.
344356
@@ -421,6 +433,7 @@ def make_static_route_table(app):
421433
return table
422434

423435

436+
@token_auth.authenticate_access_decorator
424437
async def matlab_view(req):
425438
"""API Endpoint which proxies requests to the MATLAB Embedded Connector
426439

matlab_proxy/app_state.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,21 @@ async def get_matlab_state(self):
271271
# If execution reaches here, it implies that:
272272
# 1) MATLAB process has started.
273273
# 2) Embedded connector has not started yet.
274+
# Proceed to query the Embedded Connector about its state.
275+
# matlab-proxy sends a request to itself to the endpoint: /messageservice/json/state
276+
# which the server redirects to the matlab_view() function to handle (which then sends the request to EC)
277+
# As the matlab_view is now a protected endpoint, we need to pass token information through headers.
278+
279+
# Include token information into the headers if authentication is enabled.
280+
headers = (
281+
{self.settings["mwi_auth_token_name"]: self.settings["mwi_auth_token"]}
282+
if self.settings["mwi_is_token_auth_enabled"]
283+
else None
284+
)
274285

275286
embedded_connector_status = await mwi.embedded_connector.request.get_state(
276-
self.settings["mwi_server_url"]
287+
mwi_server_url=self.settings["mwi_server_url"],
288+
headers=headers,
277289
)
278290

279291
# Embedded Connector can be in either "up" or "down" state

matlab_proxy/util/mwi/embedded_connector/request.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99
from matlab_proxy.util.mwi.exceptions import EmbeddedConnectorError
1010

11+
from matlab_proxy.util import mwi
12+
13+
logger = mwi.logger.get()
14+
1115
from .helpers import get_data_for_ping_request, get_ping_endpoint
1216

1317

@@ -39,9 +43,14 @@ async def send_request(url: str, data: dict, method: str, headers: dict = None)
3943

4044
try:
4145
async with aiohttp.ClientSession() as session:
46+
logger.debug(
47+
f"sending request: method={method}, url={url}, data={data}, headers={headers}, "
48+
)
49+
4250
async with session.request(
43-
method=method, url=url, data=data, headers=None, ssl=False
51+
method=method, url=url, data=data, headers=headers, ssl=False
4452
) as resp:
53+
logger.debug(f"response from endpoint{url} and resp={resp}")
4554
if not resp.ok:
4655
# Converting to dict and formatting for printing
4756
data = json.loads(data)
@@ -55,19 +64,25 @@ async def send_request(url: str, data: dict, method: str, headers: dict = None)
5564
raise err
5665

5766

58-
async def get_state(mwi_server_url):
67+
async def get_state(mwi_server_url, headers=None):
5968
"""Returns the state of MATLAB's Embedded Connector.
6069
6170
Args:
6271
port (int): The port on which the embedded connector is running at
63-
72+
headers: Headers to include with the request
6473
Returns:
6574
str: Either "up" or "down"
6675
"""
6776
data = get_data_for_ping_request()
6877
url = get_ping_endpoint(mwi_server_url)
78+
6979
try:
70-
resp = await send_request(url=url, data=data, method="POST")
80+
resp = await send_request(
81+
url=url,
82+
data=data,
83+
method="POST",
84+
headers=headers,
85+
)
7186

7287
# Additional assert statements to catch any changes in response from embedded connector
7388
# Tested from R2020b to R2023a

0 commit comments

Comments
 (0)