carta-controller 3.0.0-beta.1d → 3.0.0-dev.20220301
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/config/config_schema.json +29 -3
- package/config/example_backend.json +7 -0
- package/config/preference_backend_schema_2.json +105 -0
- package/dist/auth/index.js +13 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/ldap.js +3 -3
- package/dist/auth/ldap.js.map +1 -1
- package/dist/auth/local.js +38 -17
- package/dist/auth/local.js.map +1 -1
- package/dist/auth/pam.js +1 -1
- package/dist/auth/pam.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/controllerTests.js +17 -17
- package/dist/database.js +9 -9
- package/dist/index.js +11 -10
- package/dist/serverHandlers.js +54 -17
- package/dist/types.js +7 -0
- package/docs/src/centos8_instructions.rst +248 -0
- package/docs/src/conf.py +1 -1
- package/docs/src/configuration.rst +58 -4
- package/docs/src/index.rst +7 -4
- package/docs/src/installation.rst +2 -2
- package/docs/src/introduction.rst +1 -1
- package/docs/src/schema_backend.rst +7 -0
- package/docs/src/ubuntu_focal_instructions.rst +2 -2
- package/package.json +3 -3
- package/public/dashboard.js +2 -2
- package/views/templated.pug +1 -1
package/dist/database.js
CHANGED
|
@@ -24,7 +24,7 @@ const preferenceSchema = require("../config/preference_schema_2.json");
|
|
|
24
24
|
const layoutSchema = require("../config/layout_schema_2.json");
|
|
25
25
|
const snippetSchema = require("../config/snippet_schema.json");
|
|
26
26
|
const ajv = new ajv_1.default({ useDefaults: true, strictTypes: false });
|
|
27
|
-
ajv_formats_1.default(ajv);
|
|
27
|
+
(0, ajv_formats_1.default)(ajv);
|
|
28
28
|
const validatePreferences = ajv.compile(preferenceSchema);
|
|
29
29
|
const validateLayout = ajv.compile(layoutSchema);
|
|
30
30
|
const validateSnippet = ajv.compile(snippetSchema);
|
|
@@ -72,7 +72,7 @@ function initDB() {
|
|
|
72
72
|
console.log(`Connected to server ${config_1.ServerConfig.database.uri} and database ${config_1.ServerConfig.database.databaseName}`);
|
|
73
73
|
}
|
|
74
74
|
catch (err) {
|
|
75
|
-
util_1.verboseError(err);
|
|
75
|
+
(0, util_1.verboseError)(err);
|
|
76
76
|
console.error("Error connecting to database");
|
|
77
77
|
process.exit(1);
|
|
78
78
|
}
|
|
@@ -102,7 +102,7 @@ function handleGetPreferences(req, res, next) {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
catch (err) {
|
|
105
|
-
util_1.verboseError(err);
|
|
105
|
+
(0, util_1.verboseError)(err);
|
|
106
106
|
return next({ statusCode: 500, message: "Problem retrieving preferences" });
|
|
107
107
|
}
|
|
108
108
|
});
|
|
@@ -137,7 +137,7 @@ function handleSetPreferences(req, res, next) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
catch (err) {
|
|
140
|
-
util_1.verboseError(err);
|
|
140
|
+
(0, util_1.verboseError)(err);
|
|
141
141
|
return next({ statusCode: 500, message: err.errmsg });
|
|
142
142
|
}
|
|
143
143
|
});
|
|
@@ -170,7 +170,7 @@ function handleClearPreferences(req, res, next) {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
catch (err) {
|
|
173
|
-
util_1.verboseError(err);
|
|
173
|
+
(0, util_1.verboseError)(err);
|
|
174
174
|
return next({ statusCode: 500, message: "Problem clearing preferences" });
|
|
175
175
|
}
|
|
176
176
|
});
|
|
@@ -194,7 +194,7 @@ function handleGetLayouts(req, res, next) {
|
|
|
194
194
|
res.json({ success: true, layouts });
|
|
195
195
|
}
|
|
196
196
|
catch (err) {
|
|
197
|
-
util_1.verboseError(err);
|
|
197
|
+
(0, util_1.verboseError)(err);
|
|
198
198
|
return next({ statusCode: 500, message: "Problem retrieving layouts" });
|
|
199
199
|
}
|
|
200
200
|
});
|
|
@@ -229,7 +229,7 @@ function handleSetLayout(req, res, next) {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
catch (err) {
|
|
232
|
-
util_1.verboseError(err);
|
|
232
|
+
(0, util_1.verboseError)(err);
|
|
233
233
|
return next({ statusCode: 500, message: err.errmsg });
|
|
234
234
|
}
|
|
235
235
|
});
|
|
@@ -278,7 +278,7 @@ function handleGetSnippets(req, res, next) {
|
|
|
278
278
|
res.json({ success: true, snippets });
|
|
279
279
|
}
|
|
280
280
|
catch (err) {
|
|
281
|
-
util_1.verboseError(err);
|
|
281
|
+
(0, util_1.verboseError)(err);
|
|
282
282
|
return next({ statusCode: 500, message: "Problem retrieving snippets" });
|
|
283
283
|
}
|
|
284
284
|
});
|
|
@@ -313,7 +313,7 @@ function handleSetSnippet(req, res, next) {
|
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
catch (err) {
|
|
316
|
-
util_1.verboseError(err);
|
|
316
|
+
(0, util_1.verboseError)(err);
|
|
317
317
|
return next({ statusCode: 500, message: err.errmsg });
|
|
318
318
|
}
|
|
319
319
|
});
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ const config_1 = require("./config");
|
|
|
29
29
|
const controllerTests_1 = require("./controllerTests");
|
|
30
30
|
const logSymbols = require("log-symbols");
|
|
31
31
|
if (config_1.testUser) {
|
|
32
|
-
controllerTests_1.runTests(config_1.testUser).then(() => {
|
|
32
|
+
(0, controllerTests_1.runTests)(config_1.testUser).then(() => {
|
|
33
33
|
console.log(chalk.green.bold(`Controller tests with user ${config_1.testUser} succeeded`));
|
|
34
34
|
process.exit(0);
|
|
35
35
|
}, err => {
|
|
@@ -41,16 +41,15 @@ if (config_1.testUser) {
|
|
|
41
41
|
else {
|
|
42
42
|
let app = express();
|
|
43
43
|
app.use(bodyParser.urlencoded({ extended: true }));
|
|
44
|
-
app.use(bodyParser.json());
|
|
45
44
|
app.use(cookieParser());
|
|
46
45
|
app.use(bearerToken());
|
|
47
46
|
app.use(cors());
|
|
48
47
|
app.use(compression());
|
|
49
48
|
app.set("view engine", "pug");
|
|
50
49
|
app.set("views", path.join(__dirname, "../views"));
|
|
51
|
-
app.use("/api/auth", auth_1.authRouter);
|
|
52
|
-
app.use("/api/server", serverHandlers_1.serverRouter);
|
|
53
|
-
app.use("/api/database", database_1.databaseRouter);
|
|
50
|
+
app.use("/api/auth", bodyParser.json(), auth_1.authRouter);
|
|
51
|
+
app.use("/api/server", bodyParser.json(), serverHandlers_1.serverRouter);
|
|
52
|
+
app.use("/api/database", bodyParser.json(), database_1.databaseRouter);
|
|
54
53
|
app.use("/config", (req, res) => {
|
|
55
54
|
return res.json(config_1.RuntimeConfig);
|
|
56
55
|
});
|
|
@@ -91,7 +90,7 @@ else {
|
|
|
91
90
|
return res.redirect((_c = config_1.ServerConfig.serverAddress) !== null && _c !== void 0 ? _c : "");
|
|
92
91
|
}
|
|
93
92
|
});
|
|
94
|
-
app.get("/dashboard",
|
|
93
|
+
app.get("/dashboard", (req, res) => {
|
|
95
94
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
96
95
|
res.render("templated", {
|
|
97
96
|
clientId: (_a = config_1.ServerConfig.authProviders.google) === null || _a === void 0 ? void 0 : _a.clientId,
|
|
@@ -105,6 +104,9 @@ else {
|
|
|
105
104
|
});
|
|
106
105
|
});
|
|
107
106
|
app.use("/dashboard", express.static(path.join(__dirname, "../public")));
|
|
107
|
+
// Scripting proxy
|
|
108
|
+
const backendProxy = httpProxy.createServer({ ws: true });
|
|
109
|
+
app.post("/api/scripting/*", auth_1.authGuard, (0, serverHandlers_1.createScriptingProxyHandler)(backendProxy));
|
|
108
110
|
// Simplified error handling
|
|
109
111
|
app.use((err, req, res, next) => {
|
|
110
112
|
err.statusCode = err.statusCode || 500;
|
|
@@ -114,10 +116,9 @@ else {
|
|
|
114
116
|
message: err.message
|
|
115
117
|
});
|
|
116
118
|
});
|
|
117
|
-
const expressServer = http.createServer(app);
|
|
118
|
-
const backendProxy = httpProxy.createServer({ ws: true });
|
|
119
119
|
// Handle WS connections
|
|
120
|
-
expressServer
|
|
120
|
+
const expressServer = http.createServer(app);
|
|
121
|
+
expressServer.on("upgrade", (0, serverHandlers_1.createUpgradeHandler)(backendProxy));
|
|
121
122
|
// Handle WS disconnects
|
|
122
123
|
backendProxy.on("error", (err) => {
|
|
123
124
|
// Ignore connection resets
|
|
@@ -131,7 +132,7 @@ else {
|
|
|
131
132
|
});
|
|
132
133
|
function init() {
|
|
133
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
134
|
-
yield database_1.initDB();
|
|
135
|
+
yield (0, database_1.initDB)();
|
|
135
136
|
const onListenStart = () => {
|
|
136
137
|
console.log(`Started listening for login requests on port ${config_1.ServerConfig.serverPort}`);
|
|
137
138
|
};
|
package/dist/serverHandlers.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.serverRouter = exports.createUpgradeHandler = void 0;
|
|
12
|
+
exports.serverRouter = exports.createScriptingProxyHandler = exports.createUpgradeHandler = void 0;
|
|
13
13
|
const express = require("express");
|
|
14
14
|
const url = require("url");
|
|
15
15
|
const fs = require("fs");
|
|
@@ -81,7 +81,7 @@ function nextAvailablePort() {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
catch (err) {
|
|
84
|
-
util_1.verboseError(err);
|
|
84
|
+
(0, util_1.verboseError)(err);
|
|
85
85
|
console.log(`Error checking status for port ${p}: ${err.message}`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -140,14 +140,14 @@ function handleStartServer(req, res, next) {
|
|
|
140
140
|
const existingProcess = processMap.get(username);
|
|
141
141
|
if (existingProcess) {
|
|
142
142
|
// Kill the process via the kill script
|
|
143
|
-
child_process_1.spawnSync("sudo", ["-u", `${username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
143
|
+
(0, child_process_1.spawnSync)("sudo", ["-u", `${username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
144
144
|
// Delay to allow the parent process to exit
|
|
145
|
-
yield util_1.delay(10);
|
|
145
|
+
yield (0, util_1.delay)(10);
|
|
146
146
|
deleteProcess(username);
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
catch (e) {
|
|
150
|
-
util_1.verboseError(e);
|
|
150
|
+
(0, util_1.verboseError)(e);
|
|
151
151
|
console.log(`Error killing existing process belonging to user ${username}`);
|
|
152
152
|
return next({ statusCode: 400, message: "Problem killing existing process" });
|
|
153
153
|
}
|
|
@@ -162,7 +162,7 @@ function handleStartServer(req, res, next) {
|
|
|
162
162
|
return res.json({ success: true });
|
|
163
163
|
}
|
|
164
164
|
catch (e) {
|
|
165
|
-
util_1.verboseError(e);
|
|
165
|
+
(0, util_1.verboseError)(e);
|
|
166
166
|
return next(e);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
@@ -199,8 +199,8 @@ function startServer(username) {
|
|
|
199
199
|
}
|
|
200
200
|
// Finally, add the positional argument for the base folder
|
|
201
201
|
args.push(config_1.ServerConfig.baseFolderTemplate.replace("{username}", username));
|
|
202
|
-
const headerToken = uuid_1.v4();
|
|
203
|
-
const child = child_process_1.spawn("sudo", args, { env: { CARTA_AUTH_TOKEN: headerToken } });
|
|
202
|
+
const headerToken = (0, uuid_1.v4)();
|
|
203
|
+
const child = (0, child_process_1.spawn)("sudo", args, { env: { CARTA_AUTH_TOKEN: headerToken } });
|
|
204
204
|
setPendingProcess(username, port, headerToken, child);
|
|
205
205
|
let logLocation;
|
|
206
206
|
if (config_1.ServerConfig.logFileTemplate) {
|
|
@@ -219,7 +219,7 @@ function startServer(username) {
|
|
|
219
219
|
});
|
|
220
220
|
}
|
|
221
221
|
catch (err) {
|
|
222
|
-
util_1.verboseError(err);
|
|
222
|
+
(0, util_1.verboseError)(err);
|
|
223
223
|
console.error(`Could not create log file at ${logLocation}. Please ensure folder exists and permissions are set correctly`);
|
|
224
224
|
}
|
|
225
225
|
}
|
|
@@ -242,7 +242,7 @@ function startServer(username) {
|
|
|
242
242
|
logStream === null || logStream === void 0 ? void 0 : logStream.end();
|
|
243
243
|
});
|
|
244
244
|
// Check for early exit of backend process
|
|
245
|
-
yield util_1.delay(config_1.ServerConfig.startDelay);
|
|
245
|
+
yield (0, util_1.delay)(config_1.ServerConfig.startDelay);
|
|
246
246
|
if (child.exitCode || child.signalCode) {
|
|
247
247
|
throw { statusCode: 500, message: `Problem starting process for user ${username}` };
|
|
248
248
|
}
|
|
@@ -253,7 +253,7 @@ function startServer(username) {
|
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
catch (e) {
|
|
256
|
-
util_1.verboseError(e);
|
|
256
|
+
(0, util_1.verboseError)(e);
|
|
257
257
|
console.log(`Problem starting process for user ${username}`);
|
|
258
258
|
logStream === null || logStream === void 0 ? void 0 : logStream.end();
|
|
259
259
|
if (e.statusCode && e.message) {
|
|
@@ -276,9 +276,9 @@ function handleStopServer(req, res, next) {
|
|
|
276
276
|
if (existingProcess) {
|
|
277
277
|
existingProcess.process.removeAllListeners();
|
|
278
278
|
// Kill the process via the kill script
|
|
279
|
-
child_process_1.spawnSync("sudo", ["-u", `${req.username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
279
|
+
(0, child_process_1.spawnSync)("sudo", ["-u", `${req.username}`, config_1.ServerConfig.killCommand, `${existingProcess.process.pid}`]);
|
|
280
280
|
// Delay to allow the parent process to exit
|
|
281
|
-
yield util_1.delay(10);
|
|
281
|
+
yield (0, util_1.delay)(10);
|
|
282
282
|
console.log(`Process with PID ${existingProcess.process.pid} for user ${req.username} exited via stop request`);
|
|
283
283
|
deleteProcess(req.username);
|
|
284
284
|
res.json({ success: true });
|
|
@@ -288,7 +288,7 @@ function handleStopServer(req, res, next) {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
catch (e) {
|
|
291
|
-
util_1.verboseError(e);
|
|
291
|
+
(0, util_1.verboseError)(e);
|
|
292
292
|
console.log(`Error killing existing process belonging to user ${req.username}`);
|
|
293
293
|
return next({ statusCode: 500, message: "Problem killing existing process" });
|
|
294
294
|
}
|
|
@@ -311,14 +311,14 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
311
311
|
console.log(`Incoming Websocket upgrade request is missing an authentication token`);
|
|
312
312
|
return socket.end();
|
|
313
313
|
}
|
|
314
|
-
const token = yield auth_1.verifyToken(tokenString);
|
|
314
|
+
const token = yield (0, auth_1.verifyToken)(tokenString);
|
|
315
315
|
if (!token || !token.username) {
|
|
316
316
|
console.log(`Incoming Websocket upgrade request has an invalid token`);
|
|
317
317
|
return socket.end();
|
|
318
318
|
}
|
|
319
319
|
const remoteAddress = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a["x-forwarded-for"]) || ((_b = req.connection) === null || _b === void 0 ? void 0 : _b.remoteAddress);
|
|
320
320
|
console.log(`WS upgrade request from ${remoteAddress} for authenticated user ${token.username}`);
|
|
321
|
-
const username = auth_1.getUser(token.username, token.iss);
|
|
321
|
+
const username = (0, auth_1.getUser)(token.username, token.iss);
|
|
322
322
|
if (!username) {
|
|
323
323
|
console.log(`Could not find username ${token.username} in the user map`);
|
|
324
324
|
return socket.end();
|
|
@@ -333,7 +333,7 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
333
333
|
if (existingProcess && !existingProcess.process.signalCode) {
|
|
334
334
|
if (!existingProcess.ready) {
|
|
335
335
|
// Wait until existing process is ready
|
|
336
|
-
yield util_1.delay(config_1.ServerConfig.startDelay);
|
|
336
|
+
yield (0, util_1.delay)(config_1.ServerConfig.startDelay);
|
|
337
337
|
}
|
|
338
338
|
console.log(`Redirecting to backend process for ${username} (port ${existingProcess.port})`);
|
|
339
339
|
req.headers["carta-auth-token"] = existingProcess.headerToken;
|
|
@@ -352,6 +352,43 @@ const createUpgradeHandler = (server) => (req, socket, head) => __awaiter(void 0
|
|
|
352
352
|
}
|
|
353
353
|
});
|
|
354
354
|
exports.createUpgradeHandler = createUpgradeHandler;
|
|
355
|
+
const createScriptingProxyHandler = (server) => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
|
356
|
+
var _d, _e, _f;
|
|
357
|
+
const username = req === null || req === void 0 ? void 0 : req.username;
|
|
358
|
+
if (!username) {
|
|
359
|
+
return next({ statusCode: 401, message: "Not authorized" });
|
|
360
|
+
}
|
|
361
|
+
if (!req.scripting) {
|
|
362
|
+
return next({ statusCode: 403, message: "API token supplied does not permit scripting" });
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
const remoteAddress = ((_d = req.headers) === null || _d === void 0 ? void 0 : _d["x-forwarded-for"]) || ((_e = req.connection) === null || _e === void 0 ? void 0 : _e.remoteAddress);
|
|
366
|
+
let existingProcess = processMap.get(username);
|
|
367
|
+
if (!(existingProcess === null || existingProcess === void 0 ? void 0 : existingProcess.process) || existingProcess.process.signalCode) {
|
|
368
|
+
// Attempt to start new process
|
|
369
|
+
(_f = existingProcess === null || existingProcess === void 0 ? void 0 : existingProcess.process) === null || _f === void 0 ? void 0 : _f.removeAllListeners();
|
|
370
|
+
yield startServer(username);
|
|
371
|
+
existingProcess = processMap.get(username);
|
|
372
|
+
}
|
|
373
|
+
if (existingProcess && !existingProcess.process.signalCode) {
|
|
374
|
+
if (!existingProcess.ready) {
|
|
375
|
+
// Wait until existing process is ready
|
|
376
|
+
yield (0, util_1.delay)(config_1.ServerConfig.startDelay);
|
|
377
|
+
}
|
|
378
|
+
req.headers["carta-auth-token"] = existingProcess.headerToken;
|
|
379
|
+
return server.web(req, res, { target: { host: "localhost", port: existingProcess.port } });
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
return next({ statusCode: 500, message: `Backend process could not be started for ${username}` });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
console.log(`Error proxying scripting request for ${req.username}`);
|
|
387
|
+
console.log(err);
|
|
388
|
+
return next({ statusCode: 500, message: `Error proxying scripting request for ${req.username}` });
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
exports.createScriptingProxyHandler = createScriptingProxyHandler;
|
|
355
392
|
exports.serverRouter = express.Router();
|
|
356
393
|
exports.serverRouter.post("/start", auth_1.authGuard, util_1.noCache, handleStartServer);
|
|
357
394
|
exports.serverRouter.post("/stop", auth_1.authGuard, util_1.noCache, handleStopServer);
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScriptingAccess = void 0;
|
|
4
|
+
var ScriptingAccess;
|
|
5
|
+
(function (ScriptingAccess) {
|
|
6
|
+
ScriptingAccess["Enabled"] = "enabled-all-users";
|
|
7
|
+
ScriptingAccess["Disabled"] = "disabled-all-users";
|
|
8
|
+
ScriptingAccess["OptIn"] = "opt-in";
|
|
9
|
+
})(ScriptingAccess = exports.ScriptingAccess || (exports.ScriptingAccess = {}));
|
|
3
10
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
.. _centos8_instructions:
|
|
2
|
+
|
|
3
|
+
Step-by-step instructions for CentOS 8
|
|
4
|
+
======================================
|
|
5
|
+
|
|
6
|
+
.. note::
|
|
7
|
+
|
|
8
|
+
These instructions should also work for RHEL 8, AlmaLinux, and Rocky Linux. Some changes may be necessary for RHEL 7 / CentOS 7.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
1. Install Node.js
|
|
12
|
+
~~~~~~~~~~~~~~~~~~
|
|
13
|
+
|
|
14
|
+
The CARTA controller uses `Node.js <https://nodejs.org/>`_ and supports v12, v14, and v16. Node.js can easily be installed from the CentOS 8 AppStream repository. Here we install v14, as well as the ``npm`` package manager.
|
|
15
|
+
|
|
16
|
+
.. code-block:: shell
|
|
17
|
+
|
|
18
|
+
# Install Node.js v14:
|
|
19
|
+
sudo dnf module enable nodejs:14
|
|
20
|
+
sudo dnf install -y nodejs npm
|
|
21
|
+
|
|
22
|
+
# Check it is installed and working:
|
|
23
|
+
node --version
|
|
24
|
+
npm --version
|
|
25
|
+
|
|
26
|
+
2. Install MongoDB
|
|
27
|
+
~~~~~~~~~~~~~~~~~~
|
|
28
|
+
|
|
29
|
+
The CARTA controller uses `MongoDB <https://www.mongodb.com/>`_ to store user preferences, etc.. MongoDB is not available through the default CentOS 8 repositories, but we can add a custom repository to install it more easily.
|
|
30
|
+
|
|
31
|
+
.. code-block:: shell
|
|
32
|
+
|
|
33
|
+
# Create a custom MongoDB repo file:
|
|
34
|
+
sudo cat <<EOT >> /etc/yum.repos.d/mongodb-org.repo
|
|
35
|
+
[mongodb-org-4.4]
|
|
36
|
+
name=MongoDB Repository
|
|
37
|
+
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.4/x86_64/
|
|
38
|
+
gpgcheck=1
|
|
39
|
+
enabled=1
|
|
40
|
+
gpgkey=https://www.mongodb.org/static/pgp/server-4.4.asc
|
|
41
|
+
EOT
|
|
42
|
+
|
|
43
|
+
# Install MongoDB:
|
|
44
|
+
sudo dnf update
|
|
45
|
+
sudo dnf install -y mongodb-org
|
|
46
|
+
|
|
47
|
+
# Start and enable MongoDB to run on startup:
|
|
48
|
+
sudo systemctl start mongod
|
|
49
|
+
sudo systemctl enable mongod
|
|
50
|
+
|
|
51
|
+
# Check that it is working
|
|
52
|
+
sudo systemctl status mongod
|
|
53
|
+
|
|
54
|
+
.. note::
|
|
55
|
+
|
|
56
|
+
On RHEL7/CentOS7, MongoDB v14 can be installed as follows:
|
|
57
|
+
``curl -fsSL https://rpm.nodesource.com/setup_14.x | bash - && yum install -y nodejs``
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
3. Install the CARTA controller
|
|
61
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
62
|
+
|
|
63
|
+
The easiest way to install the CARTA controller is using ``npm``.
|
|
64
|
+
|
|
65
|
+
.. code-block:: shell
|
|
66
|
+
|
|
67
|
+
sudo dnf install -y python3 make gcc-c++
|
|
68
|
+
sudo npm install -g --unsafe-perm carta-controller@beta
|
|
69
|
+
|
|
70
|
+
.. note::
|
|
71
|
+
|
|
72
|
+
The CARTA controller executable will be installed at ``/usr/local/lib/node_modules/carta-controller``.
|
|
73
|
+
The CARTA frontend will be installed at ``/usr/local/lib/node_modules/carta-controller/node_modules/carta-frontend/build``.
|
|
74
|
+
|
|
75
|
+
.. note::
|
|
76
|
+
|
|
77
|
+
Do not pass the ``--unsafe-perm`` flag to ``npm`` if using a local install.
|
|
78
|
+
|
|
79
|
+
.. note::
|
|
80
|
+
|
|
81
|
+
On RHEL7/CentOS7 the CARTA controller package can not run with the default gcc version 4.8.5 (there would be an error due to ``node-linux-pam``).
|
|
82
|
+
A work around is to install a newer GCC version from source in order to get a newer ``libstdc++.so.6``, then add the location of the newer
|
|
83
|
+
``libstdc++.so.6`` to the LD_LIBRARY_PATH. After that, the CARTA controller can run on RHEL7/CentOS7.
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
4. Install the CARTA backend
|
|
88
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
89
|
+
|
|
90
|
+
The easiest way may be to install the CARTA backend is from our cartavis RPM repository.
|
|
91
|
+
|
|
92
|
+
.. code-block:: shell
|
|
93
|
+
|
|
94
|
+
# Install the CARTA backend
|
|
95
|
+
sudo curl https://packages.cartavis.org/cartavis-el8.repo --output /etc/yum.repos.d/cartavis.repo
|
|
96
|
+
sudo dnf -y install 'dnf-command(config-manager)'
|
|
97
|
+
sudo dnf -y install epel-release
|
|
98
|
+
sudo dnf -y config-manager --set-enabled powertools
|
|
99
|
+
sudo dnf -y install carta-backend-beta
|
|
100
|
+
|
|
101
|
+
# Check that the backend can run and matches the major version number of the controller
|
|
102
|
+
/usr/bin/carta_backend --version
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
5. Install Nginx
|
|
106
|
+
~~~~~~~~~~~~~~~~
|
|
107
|
+
|
|
108
|
+
The CARTA controller requires a webserver. Here we use `NGINX <https://www.nginx.com/>`_, but Apache should work too.
|
|
109
|
+
|
|
110
|
+
.. code-block:: shell
|
|
111
|
+
|
|
112
|
+
# Install nginx:
|
|
113
|
+
sudo dnf install -y nginx
|
|
114
|
+
sudo systemctl start nginx
|
|
115
|
+
sudo systemctl enable nginx
|
|
116
|
+
sudo setsebool -P httpd_can_network_connect 1
|
|
117
|
+
sudo firewall-cmd --permanent --zone=public --add-service=http
|
|
118
|
+
sudo firewall-cmd --permanent --zone=public --add-service=https
|
|
119
|
+
sudo firewall-cmd --reload
|
|
120
|
+
|
|
121
|
+
# Set up the nginx configuration file using our sample configuration file linked below:
|
|
122
|
+
sudo cd /etc/nginx/conf.d/
|
|
123
|
+
sudo vi /etc/nginx/conf.d/carta.conf
|
|
124
|
+
sudo systemctl restart nginx
|
|
125
|
+
|
|
126
|
+
# Check it is running:
|
|
127
|
+
sudo systemctl status nginx
|
|
128
|
+
|
|
129
|
+
A :ref:`sample configuration file<example_nginx>` is provided in the configuration section. This should be adapted to your server configuration.
|
|
130
|
+
|
|
131
|
+
.. note::
|
|
132
|
+
If there are problems, you can debug with ``journactl -xe`` and by checking log files in ``/var/log/nginx/``.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
6. Create the 'carta' user and modify sudoers
|
|
136
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
137
|
+
|
|
138
|
+
For security, we recommend not to run the CARTA controller as the root user. Therefore we create a new user called ``carta``.
|
|
139
|
+
|
|
140
|
+
We will assign the group ``carta-users`` to every user account and enable them to run ``/usr/bin/carta_backend`` and the script to close the CARTA backend, ``/usr/local/bin/carta-kill-script``, by adding a custom entry to the ``sudoers`` file.
|
|
141
|
+
|
|
142
|
+
.. code-block:: shell
|
|
143
|
+
|
|
144
|
+
# Create the carta user:
|
|
145
|
+
sudo adduser carta
|
|
146
|
+
# Check everything is OK
|
|
147
|
+
id carta
|
|
148
|
+
# It should show 'uid=1000(carta) gid=1000(carta) groups=1000(carta)'
|
|
149
|
+
|
|
150
|
+
# So that log files can be written:
|
|
151
|
+
sudo mkdir -p /var/log/carta
|
|
152
|
+
sudo chown -R carta /var/log/carta
|
|
153
|
+
|
|
154
|
+
# Add the custom sudoers file entry using our sample linked below
|
|
155
|
+
sudo visudo -f /etc/sudoers.d/carta_controller
|
|
156
|
+
|
|
157
|
+
An :ref:`example sudoers configuration<example_sudoers>` is provided in the configuration section.
|
|
158
|
+
|
|
159
|
+
.. note::
|
|
160
|
+
The only safe way to modify sudoers is using ``visudo``. Any syntax errors from directly editing sudoers could make your system unusable.
|
|
161
|
+
|
|
162
|
+
.. note::
|
|
163
|
+
The ``carta`` user should not be in the ``carta-users`` group. ``carta-users`` should only be assigned to the normal user accounts.
|
|
164
|
+
|
|
165
|
+
7. Set up the user authentication method
|
|
166
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
167
|
+
|
|
168
|
+
This is the most difficult step and depends on how you authenticate users at your institute. In this step-by-step guide we use PAM local authentication and a local user, ``bob``, on the server running the CARTA controller. The user ``bob`` needs to be part of the ``carta-users`` group.
|
|
169
|
+
|
|
170
|
+
With PAM authentication, the ``carta`` user that runs the CARTA controller requires access to the ``/etc/shadow`` file in order to authenticate other users. We can enable this by creating a new group called ``shadow`` and assigning the ``/etc/shadow`` file to that group.
|
|
171
|
+
|
|
172
|
+
.. note::
|
|
173
|
+
Only PAM with local authentication requires ``/etc/shadow`` access. PAM using LDAP, and Google OAuth, do not require ``/etc/shadow`` access.
|
|
174
|
+
|
|
175
|
+
.. code-block:: shell
|
|
176
|
+
|
|
177
|
+
# Create the test user 'bob':
|
|
178
|
+
sudo useradd -G carta-users bob
|
|
179
|
+
sudo passwd bob
|
|
180
|
+
|
|
181
|
+
# A new group called 'shadow' needs to be assinged to the /etc/shadow file and user 'carta':
|
|
182
|
+
sudo groupadd shadow
|
|
183
|
+
sudo chgrp shadow /etc/shadow
|
|
184
|
+
sudo chmod g+r /etc/shadow
|
|
185
|
+
sudo usermod -a -G shadow carta
|
|
186
|
+
ls -l /etc/shadow
|
|
187
|
+
# It should show permissions as ----r-----. 1 root shadow
|
|
188
|
+
# It could be helpful to reboot the server at this point
|
|
189
|
+
sudo reboot
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
8. Configure the CARTA controller
|
|
193
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
194
|
+
|
|
195
|
+
Create and fill in the ``config.json`` using our :ref:`sample configuration file<example_config>`.
|
|
196
|
+
Also generate private/public keys as they are used by the CARTA controller to sign/verify/refresh access tokens.
|
|
197
|
+
|
|
198
|
+
.. code-block:: shell
|
|
199
|
+
|
|
200
|
+
sudo mkdir /etc/carta
|
|
201
|
+
sudo chown -R carta /etc/carta
|
|
202
|
+
vi /etc/carta/config.json
|
|
203
|
+
|
|
204
|
+
# Generate private/public keys:
|
|
205
|
+
cd /etc/carta
|
|
206
|
+
sudo openssl genrsa -out carta_private.pem 4096
|
|
207
|
+
sudo openssl rsa -in carta_private.pem -outform PEM -pubout -out carta_public.pem
|
|
208
|
+
|
|
209
|
+
Please check the `CARTA Configuration Schema <https://carta-controller.readthedocs.io/en/latest/schema.html#schema>`_ for all available options.
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
9. Check everything is working
|
|
213
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
214
|
+
|
|
215
|
+
Here we switch to the ``carta`` user and test the CARTA controller with our test user ``bob``:
|
|
216
|
+
|
|
217
|
+
.. code-block:: shell
|
|
218
|
+
|
|
219
|
+
su - carta
|
|
220
|
+
carta-controller -verbose -test bob
|
|
221
|
+
|
|
222
|
+
If the test is successful, the CARTA controller should be ready to deploy.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
10. Start the CARTA controller
|
|
226
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
227
|
+
|
|
228
|
+
.. code-block:: shell
|
|
229
|
+
|
|
230
|
+
su - carta
|
|
231
|
+
carta-controller
|
|
232
|
+
|
|
233
|
+
Now your users should be able to access your server's URL and log into CARTA.
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
Optional: Set up the CARTA controller to run with pm2
|
|
237
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
238
|
+
|
|
239
|
+
`pm2 <https://pm2.keymetrics.io/>`_ is a very convenient tool to keep the CARTA controller service running in the background, and even start it up automatically after a reboot.
|
|
240
|
+
|
|
241
|
+
.. code-block:: shell
|
|
242
|
+
|
|
243
|
+
sudo npm install -g pm2
|
|
244
|
+
su -carta
|
|
245
|
+
pm2 start carta-controller
|
|
246
|
+
|
|
247
|
+
Please refer to the `pm2 documentation <https://pm2.keymetrics.io/docs/usage/startup/>`_ for detailed instructions.
|
|
248
|
+
|
package/docs/src/conf.py
CHANGED
|
@@ -22,7 +22,7 @@ copyright = '2021, Angus Comrie, Adrianna Pińska and Robert Simmonds'
|
|
|
22
22
|
author = 'Angus Comrie, Adrianna Pińska and Robert Simmonds'
|
|
23
23
|
|
|
24
24
|
# The full version, including alpha/beta/rc tags
|
|
25
|
-
release = '3.0.0-beta.
|
|
25
|
+
release = '3.0.0-beta.2'
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
# -- General configuration ---------------------------------------------------
|