Skip to content

Commit 57badbb

Browse files
authored
Merge pull request #8673 from naveenpaul1/secure-metrics-port
SSL | Secure service for Noobaa metric
2 parents e24c627 + 12d9107 commit 57badbb

File tree

9 files changed

+257
-131
lines changed

9 files changed

+257
-131
lines changed

config.js

+3
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@ config.WS_METRICS_SERVER_PORT = 7001;
594594
config.BG_METRICS_SERVER_PORT = 7002;
595595
config.HA_METRICS_SERVER_PORT = 7003;
596596
config.EP_METRICS_SERVER_PORT = 7004;
597+
config.EP_METRICS_SERVER_SSL_PORT = 9443;
597598

598599
//////////////////////////////
599600
// OAUTH RELATES //
@@ -916,6 +917,8 @@ config.ENDPOINT_SSL_PORT = Number(process.env.ENDPOINT_SSL_PORT) || 6443;
916917
config.ENDPOINT_SSL_STS_PORT = Number(process.env.ENDPOINT_SSL_STS_PORT) || (process.env.NC_NSFS_NO_DB_ENV === 'true' ? -1 : 7443);
917918
config.ENDPOINT_SSL_IAM_PORT = Number(process.env.ENDPOINT_SSL_IAM_PORT) || -1;
918919
config.ALLOW_HTTP = false;
920+
config.ALLOW_HTTP_METRICS = true;
921+
config.ALLOW_HTTPS_METRICS = true;
919922
// config files should allow access to the owner of the files
920923
config.BASE_MODE_CONFIG_FILE = 0o600;
921924
config.BASE_MODE_CONFIG_DIR = 0o700;

docs/NooBaaNonContainerized/ConfigFileCustomizations.md

+28
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,34 @@ Warning: After setting this configuration, NooBaa will skip schema validations a
450450
3. systemctl restart noobaa
451451
```
452452
453+
### 31. Prometheus HTTP enable flag -
454+
* <u>Key</u>: `ALLOW_HTTP_METRICS`
455+
* <u>Type</u>: Boolean
456+
* <u>Default</u>: true
457+
* <u>Description</u>: This flag will decide whether to enable HTTP service for Prometheus metrics.
458+
* <u>Steps</u>:
459+
```
460+
1. Open /path/to/config_dir/config.json file.
461+
2. Set the config key -
462+
Example:
463+
"ALLOW_HTTP_METRICS": true
464+
3. systemctl restart noobaa
465+
```
466+
467+
### 32. Prometheus HTTPS enable flag -
468+
* <u>Key</u>: `ALLOW_HTTPS_METRICS`
469+
* <u>Type</u>: Boolean
470+
* <u>Default</u>: true
471+
* <u>Description</u>: This flag will decide whether to enable HTTPS service for Prometheus metrics.
472+
* <u>Steps</u>:
473+
```
474+
1. Open /path/to/config_dir/config.json file.
475+
2. Set the config key -
476+
Example:
477+
"ALLOW_HTTPS_METRICS": true
478+
3. systemctl restart noobaa
479+
```
480+
453481
## Config.json File Examples
454482
The following is an example of a config.json file -
455483

docs/NooBaaNonContainerized/Monitoring.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,27 @@ Read more about NSFS metrics at - [NSFS metrics design](./../design/NSFSMetrics.
1616

1717
This section provides details about the metrics URL and port configuration necessary for accessing and monitoring system metrics.
1818

19-
#### Prometheus Metrics URL - </br>
20-
- NooBaa exports the system statistics via the following URL - </br> `http://{host}:{metrics_port}/metrics/nsfs_stats`
19+
#### Prometheus Metrics HTTP URL - </br>
20+
- NooBaa exports the system statistics via the following URL - </br> `http://{host}:{http_metrics_port}/metrics/nsfs_stats`
2121

2222
- Default port - 7004
2323

2424
- Prometheus metrics port configuration - </br> Changing Prometheus metrics port can be done by changing EP_METRICS_SERVER_PORT in config.json. </br>
25+
26+
- Prometheus metrics HTTP service can be enabled/disabled by changing `ALLOW_HTTP_METRICS` in config.json for Non Containerized Noobaa, for containerized deployments HTTP is always enabled.
27+
28+
#### Prometheus Metrics HTTPS URL - </br>
29+
30+
- NooBaa exports the system statistics via the following SSL URL - </br> `https://{host}:{https_metrics_port}/metrics/nsfs_stats`
31+
32+
- Default port - 9443
33+
34+
- Prometheus metrics port configuration - </br> Changing Prometheus metrics HTTPS port can be done by changing EP_METRICS_SERVER_SSL_PORT in config.json. </br>
35+
36+
- Prometheus metrics HTTP service can be enabled/disabled by changing `ALLOW_HTTPS_METRICS` in config.json
37+
38+
- Secure Prometheus metrics will reuse the existing S3 certificates from cert path S3_SERVICE_CERT_PATH (`/etc/s3-secret`) for containerized deployments and `{nsfs_config_root}/certificates/` for non-containerized NSFS deployments. </br> Prometheus metrics SSL cert dir path can be changed by updating S3_SERVICE_CERT_PATH in config.json. for containerized deployments </br>
39+
2540
For more details about configuring metrics port see - [Non Containerized NooBaa Developer Customization](./ConfigFileCustomizations.md)
2641

2742

src/cmd/nsfs.js

+14-10
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,14 @@ Arguments:
7171
const OPTIONS = `
7272
Options:
7373
74-
--http_port <port> (default 6001) Set the S3 endpoint listening HTTP port to serve.
75-
--https_port <port> (default 6443) Set the S3 endpoint listening HTTPS port to serve.
76-
--https_port_sts <port> (default -1) Set the S3 endpoint listening HTTPS port for STS.
77-
--https_port_iam <port> (default -1) Set the endpoint listening HTTPS port for IAM.
78-
--metrics_port <port> (default -1) Set the metrics listening port for prometheus.
79-
--forks <n> (default none) Forks spread incoming requests (config.ENDPOINT_FORKS used if flag is not provided).
80-
--debug <level> (default 0) Increase debug level.
74+
--http_port <port> (default 6001) Set the S3 endpoint listening HTTP port to serve.
75+
--https_port <port> (default 6443) Set the S3 endpoint listening HTTPS port to serve.
76+
--https_port_sts <port> (default -1) Set the S3 endpoint listening HTTPS port for STS.
77+
--https_port_iam <port> (default -1) Set the endpoint listening HTTPS port for IAM.
78+
--http_metrics_port <port> (default 7004) Set the metrics listening HTTP port for prometheus.
79+
--https_metrics_port <port> (default 9443) Set the metrics listening HTTPS port for prometheus.
80+
--forks <n> (default none) Forks spread incoming requests (config.ENDPOINT_FORKS used if flag is not provided).
81+
--debug <level> (default 0) Increase debug level.
8182
8283
## single user mode
8384
@@ -265,7 +266,8 @@ async function main(argv = minimist(process.argv.slice(2))) {
265266
const https_port = Number(argv.https_port) || config.ENDPOINT_SSL_PORT;
266267
const https_port_sts = Number(argv.https_port_sts) || config.ENDPOINT_SSL_STS_PORT;
267268
const https_port_iam = Number(argv.https_port_iam) || config.ENDPOINT_SSL_IAM_PORT;
268-
const metrics_port = Number(argv.metrics_port) || config.EP_METRICS_SERVER_PORT;
269+
const http_metrics_port = Number(argv.http_metrics_port) || config.EP_METRICS_SERVER_PORT;
270+
const https_metrics_port = Number(argv.https_metrics_port) || config.EP_METRICS_SERVER_SSL_PORT;
269271
const forks = Number(argv.forks) || config.ENDPOINT_FORKS;
270272
if (forks > 0) process.env.ENDPOINT_FORKS = forks.toString(); // used for argv.forks to take effect
271273
const uid = Number(argv.uid) || process.getuid();
@@ -311,7 +313,8 @@ async function main(argv = minimist(process.argv.slice(2))) {
311313
https_port,
312314
https_port_sts,
313315
https_port_iam,
314-
metrics_port,
316+
http_metrics_port,
317+
https_metrics_port,
315318
backend,
316319
forks,
317320
access_key,
@@ -340,7 +343,8 @@ async function main(argv = minimist(process.argv.slice(2))) {
340343
https_port,
341344
https_port_sts,
342345
https_port_iam,
343-
metrics_port,
346+
http_metrics_port,
347+
https_metrics_port,
344348
forks,
345349
nsfs_config_root,
346350
init_request_sdk: (req, res) => {

src/endpoint/endpoint.js

+20-101
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ const dbg = require('../util/debug_module')(__filename);
1111
if (!dbg.get_process_name()) dbg.set_process_name('Endpoint');
1212

1313
const util = require('util');
14-
const http = require('http');
15-
const https = require('https');
1614
const os = require('os');
1715

1816
const P = require('../util/promise');
@@ -28,7 +26,7 @@ const StsSDK = require('../sdk/sts_sdk');
2826
const ObjectIO = require('../sdk/object_io');
2927
const ObjectSDK = require('../sdk/object_sdk');
3028
const xml_utils = require('../util/xml_utils');
31-
const ssl_utils = require('../util/ssl_utils');
29+
const http_utils = require('../util/http_utils');
3230
const net_utils = require('../util/net_utils');
3331
const addr_utils = require('../util/addr_utils');
3432
const fork_utils = require('../util/fork_utils');
@@ -57,7 +55,8 @@ if (process.env.NOOBAA_LOG_LEVEL) {
5755
const SERVICES_TYPES_ENUM = Object.freeze({
5856
S3: 'S3',
5957
STS: 'STS',
60-
IAM: 'IAM'
58+
IAM: 'IAM',
59+
METRICS: 'METRICS'
6160
});
6261

6362
const new_umask = process.env.NOOBAA_ENDPOINT_UMASK || 0o000;
@@ -66,7 +65,7 @@ let fork_count;
6665
dbg.log0('endpoint: replacing old umask: ', old_umask.toString(8), 'with new umask: ', new_umask.toString(8));
6766

6867
/**
69-
* @typedef {http.IncomingMessage & {
68+
* @typedef {import('http').IncomingMessage & {
7069
* object_sdk?: ObjectSDK;
7170
* func_sdk?: FuncSDK;
7271
* sts_sdk?: StsSDK;
@@ -79,7 +78,7 @@ dbg.log0('endpoint: replacing old umask: ', old_umask.toString(8), 'with new uma
7978
/**
8079
* @typedef {(
8180
* req: EndpointRequest,
82-
* res: http.ServerResponse
81+
* res: import('http').ServerResponse
8382
* ) => void | Promise<void>} EndpointHandler
8483
*/
8584

@@ -89,18 +88,14 @@ dbg.log0('endpoint: replacing old umask: ', old_umask.toString(8), 'with new uma
8988
* https_port?: number;
9089
* https_port_sts?: number;
9190
* https_port_iam?: number;
92-
* metrics_port?: number;
91+
* http_metrics_port?: number;
92+
* https_metrics_port?: number;
9393
* nsfs_config_root?: string;
9494
* init_request_sdk?: EndpointHandler;
9595
* forks?: number;
9696
* }} EndpointOptions
9797
*/
9898

99-
// An internal function to prevent code duplication
100-
async function create_https_server(ssl_cert_info, honorCipherOrder, endpoint_handler) {
101-
const ssl_options = {...ssl_cert_info.cert, honorCipherOrder: honorCipherOrder};
102-
return https.createServer(ssl_options, endpoint_handler);
103-
}
10499

105100
/**
106101
* @param {EndpointOptions} options
@@ -117,7 +112,8 @@ async function main(options = {}) {
117112

118113
// the primary just forks and returns, workers will continue to serve
119114
fork_count = options.forks ?? config.ENDPOINT_FORKS;
120-
const metrics_port = options.metrics_port || config.EP_METRICS_SERVER_PORT;
115+
const http_metrics_port = options.http_metrics_port || config.EP_METRICS_SERVER_PORT;
116+
const https_metrics_port = options.https_metrics_port || config.EP_METRICS_SERVER_SSL_PORT;
121117
/**
122118
* Please notice that we can run the main in 2 states:
123119
* 1. Only the primary process runs the main (fork is 0 or undefined) - everything that
@@ -127,7 +123,8 @@ async function main(options = {}) {
127123
* fork_utils.start_workers because the primary process returns after start_workers
128124
* and the forks will continue executing the code lines in this function
129125
* */
130-
const is_workers_started_from_primary = await fork_utils.start_workers(metrics_port, fork_count);
126+
const is_workers_started_from_primary = await fork_utils.start_workers(http_metrics_port, https_metrics_port,
127+
options.nsfs_config_root, fork_count);
131128
if (is_workers_started_from_primary) return;
132129

133130
const endpoint_group_id = process.env.ENDPOINT_GROUP_ID || 'default-endpoint-group';
@@ -200,17 +197,15 @@ async function main(options = {}) {
200197
const https_port_sts = options.https_port_sts || config.ENDPOINT_SSL_STS_PORT;
201198
const https_port_iam = options.https_port_iam || config.ENDPOINT_SSL_IAM_PORT;
202199

203-
await start_server_and_cert(SERVICES_TYPES_ENUM.S3, init_request_sdk,
200+
await start_endpoint_server_and_cert(SERVICES_TYPES_ENUM.S3, init_request_sdk,
204201
{ ...options, https_port: https_port_s3, http_port: http_port_s3, virtual_hosts, bucket_logger, notification_logger });
205-
await start_server_and_cert(SERVICES_TYPES_ENUM.STS, init_request_sdk, { https_port: https_port_sts, virtual_hosts });
206-
await start_server_and_cert(SERVICES_TYPES_ENUM.IAM, init_request_sdk, { https_port: https_port_iam });
202+
await start_endpoint_server_and_cert(SERVICES_TYPES_ENUM.STS, init_request_sdk, { https_port: https_port_sts, virtual_hosts });
203+
await start_endpoint_server_and_cert(SERVICES_TYPES_ENUM.IAM, init_request_sdk, { https_port: https_port_iam });
207204

208205

209206
// START METRICS SERVER
210-
if (metrics_port > 0 && cluster.isPrimary) {
211-
dbg.log0('Starting metrics server', metrics_port);
212-
await prom_reporting.start_server(metrics_port, false);
213-
dbg.log0('Started metrics server successfully');
207+
if ((http_metrics_port > 0 || https_metrics_port > 0) && cluster.isPrimary) {
208+
await prom_reporting.start_server(http_metrics_port, https_metrics_port, false, options.nsfs_config_root);
214209
}
215210

216211
// TODO: currently NC NSFS deployments don't have internal_rpc_client nor db,
@@ -249,40 +244,26 @@ async function main(options = {}) {
249244
}
250245

251246
/**
252-
* start_server_and_cert starts the server by type and options and creates a certificate if required
247+
* start_endpoint_server_and_cert starts the server by type and options and creates a certificate if required
253248
* @param {('S3'|'IAM'|'STS')} server_type
254249
* @param {EndpointHandler} init_request_sdk
255250
* @param {{ http_port?: number, https_port?: number, virtual_hosts?: readonly string[],
256251
* bucket_logger?: PersistentLogger, notification_logger?: PersistentLogger,
257252
* nsfs_config_root?: string}} options
258253
*/
259-
async function start_server_and_cert(server_type, init_request_sdk, options = {}) {
254+
async function start_endpoint_server_and_cert(server_type, init_request_sdk, options = {}) {
260255
const { http_port, https_port, nsfs_config_root } = options;
261256
const endpoint_request_handler = create_endpoint_handler(server_type, init_request_sdk, options);
262257

263258
if (server_type === SERVICES_TYPES_ENUM.S3) {
264259
if (nsfs_config_root && !config.ALLOW_HTTP) {
265260
dbg.warn('HTTP is not allowed for NC NSFS.');
266261
} else {
267-
const http_server = http.createServer(endpoint_request_handler);
268-
if (http_port > 0) {
269-
dbg.log0(`Starting ${server_type} HTTP - ${http_port}`);
270-
await listen_http(http_port, http_server);
271-
dbg.log0(`Started ${server_type} HTTP successfully`);
272-
}
262+
await http_utils.start_http_server(http_port, server_type, endpoint_request_handler);
273263
}
274264
}
275265
if (https_port > 0) {
276-
const ssl_cert_info = await ssl_utils.get_ssl_cert_info(server_type, nsfs_config_root);
277-
const https_server = await create_https_server(ssl_cert_info, true, endpoint_request_handler);
278-
ssl_cert_info.on('update', updated_ssl_cert_info => {
279-
dbg.log0(`Setting updated ${server_type} ssl certs for endpoint.`);
280-
const updated_ssl_options = { ...updated_ssl_cert_info.cert, honorCipherOrder: true };
281-
https_server.setSecureContext(updated_ssl_options);
282-
});
283-
dbg.log0(`Starting ${server_type} HTTPS - ${https_port}`);
284-
await listen_http(https_port, https_server);
285-
dbg.log0(`Started ${server_type} HTTPS successfully`);
266+
await http_utils.start_https_server(https_port, server_type, endpoint_request_handler, nsfs_config_root);
286267
}
287268
}
288269

@@ -506,68 +487,6 @@ function unavailable_handler(req, res) {
506487
res.end(reply);
507488
}
508489

509-
function listen_http(port, server) {
510-
return new Promise((resolve, reject) => {
511-
setup_http_server(server);
512-
server.listen(port, err => {
513-
if (err) {
514-
dbg.error('ENDPOINT FAILED to listen', err);
515-
reject(err);
516-
} else {
517-
resolve();
518-
}
519-
});
520-
});
521-
}
522-
523-
function setup_http_server(server) {
524-
// Handle 'Expect' header different than 100-continue to conform with AWS.
525-
// Consider any expect value as if the client is expecting 100-continue.
526-
// See https://github.com/ceph/s3-tests/blob/master/s3tests/functional/test_headers.py:
527-
// - test_object_create_bad_expect_mismatch()
528-
// - test_object_create_bad_expect_empty()
529-
// - test_object_create_bad_expect_none()
530-
// - test_object_create_bad_expect_unreadable()
531-
// See https://nodejs.org/api/http.html#http_event_checkexpectation
532-
server.on('checkExpectation', function on_s3_check_expectation(req, res) {
533-
res.writeContinue();
534-
server.emit('request', req, res);
535-
});
536-
537-
// See https://nodejs.org/api/http.html#http_event_clienterror
538-
server.on('clientError', function on_s3_client_error(err, socket) {
539-
540-
// On parsing errors we reply 400 Bad Request to conform with AWS
541-
// These errors come from the nodejs native http parser.
542-
if (typeof err.code === 'string' &&
543-
err.code.startsWith('HPE_INVALID_') &&
544-
err.bytesParsed > 0) {
545-
console.error('ENDPOINT CLIENT ERROR - REPLY WITH BAD REQUEST', err);
546-
socket.write('HTTP/1.1 400 Bad Request\r\n');
547-
socket.write(`Date: ${new Date().toUTCString()}\r\n`);
548-
socket.write('Connection: close\r\n');
549-
socket.write('Content-Length: 0\r\n');
550-
socket.end('\r\n');
551-
}
552-
553-
// in any case we destroy the socket
554-
socket.destroy();
555-
});
556-
557-
server.keepAliveTimeout = config.ENDPOINT_HTTP_SERVER_KEEPALIVE_TIMEOUT;
558-
server.requestTimeout = config.ENDPOINT_HTTP_SERVER_REQUEST_TIMEOUT;
559-
server.maxRequestsPerSocket = config.ENDPOINT_HTTP_MAX_REQUESTS_PER_SOCKET;
560-
561-
server.on('error', handle_server_error);
562-
563-
// This was an attempt to read from the socket in large chunks,
564-
// but it seems like it has no effect and we still get small chunks
565-
// server.on('connection', function on_s3_connection(socket) {
566-
// socket._readableState.highWaterMark = 1024 * 1024;
567-
// socket.setNoDelay(true);
568-
// });
569-
}
570-
571490
exports.main = main;
572491
exports.create_endpoint_handler = create_endpoint_handler;
573492
exports.create_init_request_sdk = create_init_request_sdk;

0 commit comments

Comments
 (0)