diff --git a/cra-server/.env_sample b/cra-server/.env_sample index d383b50e..73c504a8 100644 --- a/cra-server/.env_sample +++ b/cra-server/.env_sample @@ -7,4 +7,17 @@ # # In this example the first line configures a ui server called aaa with a remote server name of "cocoView1" and URL of "https://localhost:9443" EGERIA_PRESENTATIONSERVER_SERVER_aaa={"remoteServerName":"cocoView1","remoteURL":"https://localhost:9443"} -EGERIA_PRESENTATIONSERVER_SERVER_bbb={"remoteServerName":"cocoView2","remoteURL":"https://localhost:9443"} \ No newline at end of file +EGERIA_PRESENTATIONSERVER_SERVER_bbb={"remoteServerName":"cocoView2","remoteURL":"https://localhost:9443"} + +# the following 3 environment variables relate to the presentation server to omag platform ssl security. Egeria provides defaults for these values +# for use in a development environment. Custom values can be supplied. + +# This is the passphrase used by ssl on the presentation server to omag platform sessions. +# EGERIA_SECURITY_PASSPHRASE= +# +# This should specify the file name of the certificate in the ssl folder that identifies the React ui server. It defaults to EgeriaReactUIServer.p12. +# EGERIA_CERTIFICATE_FILE_LOCATION_FOR_REACT_UI_SERVER= +# +# This should specify the file name of the certificate authority in the ssl folder. It defaults to EgeriaRootCA.p12 +# EGERIA_CERTIFICATE_FILE_LOCATION_FOR_CERTIFICATE_AUTHORITY= + diff --git a/cra-server/functions/getAxiosInstance.js b/cra-server/functions/getAxiosInstance.js index bb7c15f1..e7898223 100644 --- a/cra-server/functions/getAxiosInstance.js +++ b/cra-server/functions/getAxiosInstance.js @@ -4,13 +4,9 @@ const axios = require('axios'); const https = require("https"); const fs = require("fs"); -const path = require("path") const getServerInfoFromEnv = require('./getServerInfoFromEnv'); -const cert = fs.readFileSync(path.join(__dirname, '../../') + "ssl/keys/server.cert"); -const key = fs.readFileSync(path.join(__dirname, '../../') + "ssl/keys/server.key"); - -const getAxiosInstance = (url) => { +const getAxiosInstance = (url, ca, pfx, passphrase) => { try { @@ -30,10 +26,9 @@ const getAxiosInstance = (url) => { const instance = axios.create({ baseURL: downStreamURL, httpsAgent: new https.Agent({ - // ca: - at some stage add the certificate authority - cert: cert, - key: key, - rejectUnauthorized: false, + ca: ca, + pfx: pfx, + passphrase: passphrase }), }); return instance; diff --git a/cra-server/index.js b/cra-server/index.js index 8608dcf7..63fee876 100644 --- a/cra-server/index.js +++ b/cra-server/index.js @@ -13,7 +13,6 @@ require("dotenv").config(); const getServerInfoFromEnv = require('./functions/getServerInfoFromEnv'); const serverNameMiddleWare = require('./functions/serverNameMiddleware'); const passportConfiguration = require('./functions/passportConfiguration'); -const loggedIn = require('./functions/loggedIn'); const router = require('./router/routes'); @@ -21,7 +20,7 @@ const PORT = process.env.PORT || 8091; const env = process.env.NODE_ENV || 'development'; -// ssl self signed certificate and key +// ssl self signed certificate and key for browser session const cert = fs.readFileSync(path.join(__dirname, '../') + "ssl/keys/server.cert"); const key = fs.readFileSync(path.join(__dirname, '../') + "ssl/keys/server.key"); const options = { @@ -32,6 +31,7 @@ app.set('key', key); app.set('cert', cert); const servers = getServerInfoFromEnv(); + app.set('servers', servers); if (env === 'production') { app.use(express.static(path.join(__dirname, '../cra-client/build'))); diff --git a/cra-server/router/routes.js b/cra-server/router/routes.js index 1fc49474..770a219c 100644 --- a/cra-server/router/routes.js +++ b/cra-server/router/routes.js @@ -2,32 +2,42 @@ /* Copyright Contributors to the ODPi Egeria project. */ const express = require("express"); const router = express.Router(); -const fs = require("fs"); const path = require("path"); +const fs = require("fs"); const axios = require("axios"); const https = require("https"); const rateLimit = require("express-rate-limit"); const getAxiosInstance = require("../functions/getAxiosInstance"); +// const getSSLInfoForViewServerFromEnv = require("../functions/getSSLInfoForViewServerFromEnv"); const validateURL = require("../validations/validateURL"); const validateAdminURL = require("../validations/validateAdminURL"); +/** + * This module contains the middleware to handle the inbound requests. There is code to handle the login and code to route + * inbound rest calls to up to the appropraite service (admin or view service) running on the connected omag server. + * + */ + + let pfx_file_location = "EgeriaReactUIServer.p12"; + let ca_file_location = "EgeriaRootCA.p12"; + + // used to identify us (the Egeria React UI server) + let pfx; + // this is the certificate authority + let ca; + // this is the default password + let passphrase = "egeria"; + + + // required for codeQL to ensure that logins are rate limitted const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs }); -// used for client authentication (so we can trust the server) -const keystore = fs.readFileSync( - path.join(__dirname, "../../") + "ssl/keystore.p12" -); -// server for server authentication (so the server can trust us) -const truststore = fs.readFileSync( - path.join(__dirname, "../../") + "ssl/truststore.p12" -); -passphrase = "egeria"; /** * Middleware to handle post requests that start with /login i.e. the login request. The tenant segment has been removed by previous middleware. @@ -93,6 +103,41 @@ const joinedPath = path.join( "../../cra-client/build/", "index.html" ); +const getSSLInfoForViewServerFromEnv = () => { + + // capitals as Windows can be case sensitive. + const env_passphrase = "EGERIA_SECURITY_PASSPHRASE"; + const env_egeria_react_ui_server_file_location = + "EGERIA_CERTIFICATE_FILE_LOCATION_FOR_REACT_UI_SERVER"; + const env_egeria_certificate_authority_file_location = + "EGERIA_CERTIFICATE_FILE_LOCATION_FOR_CERTIFICATE_AUTHORITY"; + + const env = process.env; + + for (const envVariable in env) { + try { + if (envVariable === env_egeria_react_ui_server_file_location) { + pfx_file_location = env[envVariable]; + } else if ( + envVariable === env_egeria_certificate_authority_file_location + ) { + ca_file_location = env[envVariable]; + } else if (envVariable === env_passphrase) { + passphrase = env[envVariable]; + } + } catch (error) { + console.log(error); + console.log( + "Error occured processing environment variables. Ignore and carry on looking for more valid server content." + ); + } + } + pfx = fs.readFileSync(path.join(__dirname, "../../ssl/") + pfx_file_location); + ca = fs.readFileSync(path.join(__dirname, "../../ssl/") + ca_file_location); + +}; +// populate the ssl information for the view server from the environment +getSSLInfoForViewServerFromEnv(); /** * Process login url, */ @@ -111,7 +156,7 @@ router.post("/servers/*", (req, res) => { //console.log("Got body:", body); const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { - const instance = getAxiosInstance(incomingUrl); + const instance = getAxiosInstance(incomingUrl, ca, pfx, passphrase); instance .post("", body) .then(function (response) { @@ -143,7 +188,7 @@ router.put("/servers/*", (req, res) => { //console.log("Got body:", body); const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { - const instance = getAxiosInstance(incomingUrl); + const instance = getAxiosInstance(incomingUrl, ca, pfx, passphrase); instance .put("", body) .then(function (response) { @@ -172,7 +217,7 @@ router.delete("/servers/*", (req, res) => { // console.log("/servers/* delete called " + incomingUrl); const servers = req.app.get("servers"); if (validateURL(incomingUrl, servers)) { - const instance = getAxiosInstance(incomingUrl); + const instance = getAxiosInstance(incomingUrl, ca, pfx, passphrase); instance .delete() .then(function (response) { @@ -201,7 +246,7 @@ router.get("/servers/*", (req, res) => { // console.log("/servers/* get called " + url); const servers = req.app.get("servers"); if (validateURL(url, servers)) { - const instance = getAxiosInstance(url); + const instance = getAxiosInstance(url, ca, pfx, passphrase); instance .get() .then(function (response) { @@ -233,9 +278,9 @@ router.get("/open-metadata/admin-services/*", (req, res) => { method: "get", url: urlRoot + incomingPath, httpsAgent: new https.Agent({ - ca: truststore, - pfx: keystore, - passphrase: passphrase, + ca: ca, + pfx: pfx, + passphrase: passphrase }), headers: { "Access-Control-Allow-Origin": "*", @@ -277,9 +322,9 @@ router.post("/open-metadata/admin-services/*", (req, res) => { "Access-Control-Allow-Origin": "*", }, httpsAgent: new https.Agent({ - ca: truststore, - pfx: keystore, - passphrase: passphrase, + ca: ca, + pfx: pfx, + passphrase: passphrase }), }; if (config) apiReq.data = config; @@ -317,9 +362,9 @@ router.delete("/open-metadata/admin-services/*", (req, res) => { "Content-Type": "application/json", }, httpsAgent: new https.Agent({ - ca: truststore, - pfx: keystore, - passphrase: passphrase, + ca: ca, + pfx: pfx, + passphrase: passphrase }), }; if (config) apiReq.data = config; @@ -356,9 +401,9 @@ router.get("/open-metadata/platform-services/*", (req, res) => { method: "get", url: urlRoot + incomingPath, httpsAgent: new https.Agent({ - ca: truststore, - pfx: keystore, - passphrase: passphrase, + ca: ca, + pfx: pfx, + passphrase: passphrase }), }; axios(apiReq) diff --git a/ssl/EgeriaReactUIServer.p12 b/ssl/EgeriaReactUIServer.p12 new file mode 100644 index 00000000..a40d9793 Binary files /dev/null and b/ssl/EgeriaReactUIServer.p12 differ diff --git a/ssl/EgeriaRootCA.p12 b/ssl/EgeriaRootCA.p12 new file mode 100644 index 00000000..65dfaa50 Binary files /dev/null and b/ssl/EgeriaRootCA.p12 differ diff --git a/ssl/keystore.p12 b/ssl/keystore.p12 deleted file mode 100644 index b679ee3c..00000000 Binary files a/ssl/keystore.p12 and /dev/null differ diff --git a/ssl/truststore.p12 b/ssl/truststore.p12 deleted file mode 100644 index b679ee3c..00000000 Binary files a/ssl/truststore.p12 and /dev/null differ