56
56
import java .nio .file .Files ;
57
57
import java .nio .file .Path ;
58
58
import java .nio .file .Paths ;
59
+ import java .nio .file .attribute .PosixFileAttributes ;
60
+ import java .nio .file .attribute .PosixFilePermission ;
61
+ import java .nio .file .attribute .PosixFilePermissions ;
59
62
import java .util .HashSet ;
60
63
import java .util .Properties ;
61
64
import java .util .Set ;
@@ -116,7 +119,7 @@ public class JMXServlet extends HttpServlet {
116
119
}
117
120
118
121
private JMXtoXML client ;
119
- private final Set <String > localhostAddresses = new HashSet <>();
122
+ private final Set <String > serverAddresses = new HashSet <>();
120
123
121
124
private Path dataDir ;
122
125
private Path tokenFile ;
@@ -128,11 +131,15 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t
128
131
// Verify if request is from localhost or if user has specific servlet/container managed role.
129
132
if (isFromLocalHost (request )) {
130
133
// Localhost is always authorized to access
131
- LOG .debug ("Local access granted" );
134
+ if (LOG .isDebugEnabled ()) {
135
+ LOG .debug ("Local access granted" );
136
+ }
132
137
133
138
} else if (hasSecretToken (request , getToken ())) {
134
139
// Correct token is provided
135
- LOG .debug ("Correct token provided by {}" , request .getRemoteHost ());
140
+ if (LOG .isDebugEnabled ()) {
141
+ LOG .debug ("Correct token provided by {}" , request .getRemoteHost ());
142
+ }
136
143
137
144
} else {
138
145
// Check if user is already authorized, e.g. via MONEX allow user too
@@ -218,7 +225,7 @@ public void init(ServletConfig config) throws ServletException {
218
225
client .connect ();
219
226
220
227
// Register all known localhost addresses
221
- registerLocalHostAddresses ();
228
+ registerServerAddresses ();
222
229
223
230
// Get directory for token file
224
231
final String jmxDataDir = client .getDataDir ();
@@ -232,34 +239,47 @@ public void init(ServletConfig config) throws ServletException {
232
239
}
233
240
234
241
// Setup token and tokenfile
235
- obtainTokenFileReference ();
236
-
237
- LOG .info ("JMXservlet token: {}" , getToken ());
242
+ if (tokenFile == null ) {
243
+ tokenFile = dataDir .resolve (TOKEN_FILE );
244
+ LOG .info ("Token file: {}" , tokenFile .toAbsolutePath ().toAbsolutePath ());
245
+ }
238
246
247
+ // NOTE(AR) make sure to create the token in init when the servlet is loaded at startup so that it is present for Monex
248
+ final String token = getToken ();
249
+ LOG .info ("JMXServlet token: {}" , token );
239
250
}
240
251
241
252
/**
242
- * Register all known IP-addresses for localhost .
253
+ * Register all known IP-addresses for server .
243
254
*/
244
- void registerLocalHostAddresses () {
245
- // The external IP address of the server
255
+ void registerServerAddresses () {
256
+ // The IPv4 address of the loopback interface of the server - 127.0.0.1 on Windows/Linux/macOS, or 127.0.1.1 on Debian/Ubuntu
257
+ try {
258
+ serverAddresses .add (InetAddress .getLocalHost ().getHostAddress ());
259
+ } catch (final UnknownHostException ex ) {
260
+ LOG .warn ("Unable to get loopback IP address for localhost: {}" , ex .getMessage ());
261
+ }
262
+
263
+ // Any additional IPv4 and IPv6 addresses associated with the loopback interface of the server
246
264
try {
247
- localhostAddresses .add (InetAddress .getLocalHost ().getHostAddress ());
248
- } catch (UnknownHostException ex ) {
249
- LOG .warn ("Unable to get HostAddress for localhost: {}" , ex .getMessage ());
265
+ for (final InetAddress loopBackAddress : InetAddress .getAllByName ("localhost" )) {
266
+ serverAddresses .add (loopBackAddress .getHostAddress ());
267
+ }
268
+ } catch (final UnknownHostException ex ) {
269
+ LOG .warn ("Unable to retrieve additional loopback IP addresses for localhost: {}" , ex .getMessage ());
250
270
}
251
271
252
- // The configured Localhost addresses
272
+ // Any IPv4 and IPv6 addresses associated with other interfaces in the server
253
273
try {
254
- for (InetAddress address : InetAddress .getAllByName ("localhost" )) {
255
- localhostAddresses .add (address .getHostAddress ());
274
+ for (final InetAddress hostAddress : InetAddress .getAllByName (InetAddress . getLocalHost (). getHostName () )) {
275
+ serverAddresses .add (hostAddress .getHostAddress ());
256
276
}
257
- } catch (UnknownHostException ex ) {
258
- LOG .warn ("Unable to retrieve ipaddresses for localhost: {}" , ex .getMessage ());
277
+ } catch (final UnknownHostException ex ) {
278
+ LOG .warn ("Unable to retrieve additional interface IP addresses for localhost: {}" , ex .getMessage ());
259
279
}
260
280
261
- if (localhostAddresses .isEmpty ()) {
262
- LOG .error ("Unable to determine addresses for localhost, jmx servlet might be disfunctional ." );
281
+ if (serverAddresses .isEmpty ()) {
282
+ LOG .error ("Unable to determine IP addresses for localhost, JMXServlet might be dysfunctional ." );
263
283
}
264
284
}
265
285
@@ -269,8 +289,13 @@ void registerLocalHostAddresses() {
269
289
* @param request The HTTP request
270
290
* @return TRUE if request is from LOCALHOST otherwise FALSE
271
291
*/
272
- boolean isFromLocalHost (HttpServletRequest request ) {
273
- return localhostAddresses .contains (request .getRemoteAddr ());
292
+ boolean isFromLocalHost (final HttpServletRequest request ) {
293
+ String remoteAddr = request .getRemoteAddr ();
294
+ if (remoteAddr .charAt (0 ) == '[' ) {
295
+ // Handle IPv6 addresses that are wrapped in []
296
+ remoteAddr = remoteAddr .substring (1 , remoteAddr .length () - 1 );
297
+ }
298
+ return serverAddresses .contains (remoteAddr );
274
299
}
275
300
276
301
/**
@@ -291,17 +316,6 @@ boolean hasSecretToken(HttpServletRequest request, String token) {
291
316
return false ;
292
317
}
293
318
294
- /**
295
- * Obtain reference to token file
296
- */
297
- private void obtainTokenFileReference () {
298
-
299
- if (tokenFile == null ) {
300
- tokenFile = dataDir .resolve (TOKEN_FILE );
301
- LOG .info ("Token file: {}" , tokenFile .toAbsolutePath ().toAbsolutePath ());
302
- }
303
- }
304
-
305
319
/**
306
320
* Get token from file, create if not existent. Data is read for each call so the file can be updated run-time.
307
321
*
@@ -327,6 +341,13 @@ private String getToken() {
327
341
// Create and write when needed
328
342
if (!Files .exists (tokenFile ) || token == null ) {
329
343
344
+ final Set <PosixFilePermission > permissions = PosixFilePermissions .fromString ("rw-r-----" );
345
+ try {
346
+ tokenFile = Files .createFile (tokenFile , PosixFilePermissions .asFileAttribute (permissions ));
347
+ } catch (final Throwable t ) {
348
+ LOG .warn ("Unable to restrict permissions on: " + tokenFile );
349
+ }
350
+
330
351
// Create random token
331
352
token = UUIDGenerator .getUUIDversion4 ();
332
353
0 commit comments