diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 682bea54e..9a56093bc 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.3.0
+ rev: v5.0.0
hooks:
- id: check-yaml
exclude: ^(helm/wrongsecrets-ctf-party/templates/|helm/test.tmp.yaml|azure/k8s/)
@@ -12,7 +12,7 @@ repos:
exclude: ^(src/test/resources/yourkey.txt|src/test/resources/secondkey.txt)
- id: trailing-whitespace
- repo: https://github.com/antonbabenko/pre-commit-terraform
- rev: v1.71.0
+ rev: v1.96.2
hooks:
- id: terraform_fmt
- id: terraform_tflint
@@ -32,7 +32,7 @@ repos:
- "--args=--only=terraform_workspace_remote"
- id: terraform_docs
- repo: https://github.com/norwoodj/helm-docs
- rev: v1.2.0
+ rev: v1.14.2
hooks:
- id: helm-docs
args:
@@ -46,7 +46,7 @@ repos:
# A base filename makes it relative to each chart directory found
- --template-files=README.md.gotmpl
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
- rev: v9.4.0
+ rev: v9.18.0
hooks:
- id: commitlint
stages: [commit-msg]
diff --git a/aws/README.md b/aws/README.md
index 3313a9197..b9d386563 100644
--- a/aws/README.md
+++ b/aws/README.md
@@ -137,7 +137,7 @@ Note that you might have to do some manual cleanups after that.
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -240,4 +240,4 @@ The documentation below is auto-generated to give insight on what's created via
| [load\_balancer\_controller\_role\_arn](#output\_load\_balancer\_controller\_role\_arn) | Load balancer controller role arn |
| [secrets\_manager\_secret\_name](#output\_secrets\_manager\_secret\_name) | The name of the secrets manager secret |
| [state\_bucket\_name](#output\_state\_bucket\_name) | Terraform s3 state bucket name |
-
+
diff --git a/aws/shared-state/README.md b/aws/shared-state/README.md
index 5894a5ebb..caaaf1a97 100644
--- a/aws/shared-state/README.md
+++ b/aws/shared-state/README.md
@@ -1,7 +1,7 @@
# Terraform documentation
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -38,4 +38,4 @@ No modules.
|------|-------------|
| [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | Name of the terraform state bucket |
| [s3\_bucket\_name](#output\_s3\_bucket\_name) | Name of the terraform state bucket |
-
+
diff --git a/azure/README.md b/azure/README.md
index ed6efdd87..57f170960 100644
--- a/azure/README.md
+++ b/azure/README.md
@@ -145,7 +145,7 @@ Note that you might have to do some manual cleanups after that.
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -213,4 +213,4 @@ No modules.
| [tenant\_id](#output\_tenant\_id) | Azure tenant ID |
| [vault\_name](#output\_vault\_name) | Vault name |
| [vault\_uri](#output\_vault\_uri) | Vault URI |
-
+
diff --git a/azure/shared-state/README.md b/azure/shared-state/README.md
index edca17180..f48485616 100644
--- a/azure/shared-state/README.md
+++ b/azure/shared-state/README.md
@@ -1,7 +1,7 @@
# Terraform documentation
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -42,4 +42,4 @@ No modules.
| Name | Description |
|------|-------------|
| [storage\_account\_name](#output\_storage\_account\_name) | The generated storage account name |
-
+
diff --git a/gcp/README.md b/gcp/README.md
index 96f4e973d..1266ef88a 100644
--- a/gcp/README.md
+++ b/gcp/README.md
@@ -136,7 +136,7 @@ Note that you might have to do some manual cleanups after that.
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -203,4 +203,4 @@ No modules.
| [kubernetes\_cluster\_name](#output\_kubernetes\_cluster\_name) | GKE Cluster Name |
| [project\_id](#output\_project\_id) | GCloud Project ID |
| [region](#output\_region) | GCloud Region |
-
+
diff --git a/gcp/shared-state/README.md b/gcp/shared-state/README.md
index 0501a6c69..834f69122 100644
--- a/gcp/shared-state/README.md
+++ b/gcp/shared-state/README.md
@@ -1,7 +1,7 @@
# Terraform documentation
The documentation below is auto-generated to give insight on what's created via Terraform.
-
+
## Requirements
| Name | Version |
@@ -40,4 +40,4 @@ No modules.
| Name | Description |
|------|-------------|
| [bucket](#output\_bucket) | Terraform backend storage bucket |
-
+
diff --git a/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/config-map.yaml b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/config-map.yaml
index ed422412b..986eacc9c 100644
--- a/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/config-map.yaml
+++ b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/config-map.yaml
@@ -34,5 +34,32 @@ data:
"affinity": {{ .Values.wrongsecrets.affinity | toJson }},
"tolerations": {{ .Values.wrongsecrets.tolerations | toJson }},
"runtimeClassName": {{ .Values.wrongsecrets.runtimeClassName | toJson }}
+ },
+ "websocket": {
+ "servicename1:8080": [
+ "incomingurl1",
+ "incomingurl2",
+ "incomingurl3"
+ ],
+ "serviname2:3000": [
+ "incomingurl4",
+ "incomingurl5"
+ ]
+ },
+ "proxy": {
+ "servicename1:8080": [
+ "incomingurl6",
+ "incomingurl7"
+ ],
+ "servicename2:3000": [
+ "incomingurl8withwildcard"
+ ],
+ "servicename3(CTFD)": [
+ "incomingurl9",
+ "incomingurl10"
+ ],
+ "servicename4(grafana)": [
+ "incomingurl10"
+ ]
}
}
diff --git a/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/deployment.yaml b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/deployment.yaml
index 2bc983794..ef4552e2e 100644
--- a/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/deployment.yaml
+++ b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/deployment.yaml
@@ -62,15 +62,17 @@ spec:
securityContext:
{{- omit .Values.balancer.containerSecurityContext "enabled" | toYaml | nindent 12 }}
{{- end }}
- {{- if .Values.balancer.volumeMounts }}
volumeMounts:
- {{- toYaml .Values.balancer.volumeMounts | nindent 12 }}
- {{- end }}
+ - name: proxy-config
+ mountPath: /etc/config
resources:
{{- toYaml .Values.balancer.resources | nindent 12 }}
+ volumes:
+ - name: proxy-config
+ configMap:
+ name: proxy-config
{{- if .Values.balancer.volumes }}
- volumes:
- {{- toYaml .Values.balancer.volumes | nindent 8 }}
+ {{- toYaml .Values.balancer.volumes | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
diff --git a/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/proxy-config.yaml b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/proxy-config.yaml
new file mode 100644
index 000000000..2592561fe
--- /dev/null
+++ b/helm/wrongsecrets-ctf-party/templates/wrongsecrets-balancer/proxy-config.yaml
@@ -0,0 +1,25 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: proxy-config
+data:
+ # Example, adjust according to your needs
+ websocket: |
+ servicename1:8080:
+ incomingurl1
+ incomingurl2
+ incomingurl3
+ servicename2:3000
+ incomingurl4
+ incomingurl5
+ proxy: |
+ servicename1:8080:
+ incomingurl6
+ incomingurl7
+ servicename2:3000:
+ incomingurl8withwildcard
+ servicename3(CTFD):
+ incomingurl9
+ incomingurl10
+ servicename4(grafana):
+ incomingurl11
diff --git a/wrongsecrets-balancer/src/proxy/proxy-config.yaml b/wrongsecrets-balancer/src/proxy/proxy-config.yaml
new file mode 100644
index 000000000..ce8f45d1e
--- /dev/null
+++ b/wrongsecrets-balancer/src/proxy/proxy-config.yaml
@@ -0,0 +1,18 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: proxy-config
+data:
+ proxy-config.yaml: |
+ websocket:
+ servicename1:8080:
+ - /guaclite
+ - /files/socket.io/
+ servicename2:3000:
+ - /another-path
+ proxy:
+ servicename1:8080:
+ - /path1
+ - /path2
+ servicename2:3000:
+ - /another-path
diff --git a/wrongsecrets-balancer/src/proxy/proxy.js b/wrongsecrets-balancer/src/proxy/proxy.js
index 861eb430f..8b14df70e 100644
--- a/wrongsecrets-balancer/src/proxy/proxy.js
+++ b/wrongsecrets-balancer/src/proxy/proxy.js
@@ -1,201 +1,87 @@
const express = require('express');
const httpProxy = require('http-proxy');
const proxy = httpProxy.createProxyServer();
-const cookieParser = require('cookie-parser');
-
-const { get, extractTeamName } = require('../config');
+const fs = require('fs');
+const yaml = require('js-yaml');
+const path = require('path');
const { logger } = require('../logger');
-const {
- getJuiceShopInstanceForTeamname,
- updateLastRequestTimestampForTeam,
-} = require('../kubernetes');
const router = express.Router();
-/**
- * @param {import("express").Request} req
- * @param {import("express").Response} res
- * @param {import("express").NextFunction} next
- */
-function redirectJuiceShopTrafficWithoutBalancerCookies(req, res, next) {
- if (!req.teamname) {
- logger.debug('Got request without team cookie in proxy. Redirecting to /balancer/');
- return res.redirect('/balancer/');
- }
- return next();
-}
-
-/**
- * @param {import("express").Request} req
- * @param {import("express").Response} res
- * @param {import("express").NextFunction} next
- */
-function redirectAdminTrafficToBalancerPage(req, res, next) {
- if (req.teamname === `t-${get('admin.username')}`) {
- logger.debug('Got admin request in proxy. Redirecting to /balancer/');
- return res.redirect('/balancer/?msg=logged-as-admin');
- }
- return next();
-}
-
-const connectionCache = new Map();
-
-/**
- * Checks at most every 10sec if the deployment the traffic should go to is ready.
- *
- * @param {import("express").Request} req
- * @param {import("express").Response} res
- * @param {import("express").NextFunction} next
- */
-async function checkIfInstanceIsUp(req, res, next) {
- const teamname = req.cleanedTeamname;
-
- const currentTime = new Date().getTime();
- if (connectionCache.has(teamname) && currentTime - connectionCache.get(teamname) < 10000) {
- return next();
- }
-
- try {
- const { readyReplicas } = await getJuiceShopInstanceForTeamname(teamname);
-
- if (readyReplicas === 1) {
- return next();
- }
-
- logger.warn(`Tried to proxy for team ${teamname}, but no ready instance found.`);
- return res.redirect(`/balancer/?msg=instance-restarting&teamname=${teamname}`);
- } catch (error) {
- logger.warn(`Could not find instance for team: '${teamname}'`);
- logger.warn(JSON.stringify(error));
- res.redirect(`/balancer/?msg=instance-not-found&teamname=${teamname}`);
- }
-}
-
-/**
- * @param {import("express").Request} req
- * @param {import("express").Response} res
- * @param {import("express").NextFunction} next
- */
-async function updateLastConnectTimestamp(req, res, next) {
- const currentTime = new Date().getTime();
- const teamname = req.cleanedTeamname;
-
- try {
- if (connectionCache.has(teamname)) {
- const timeDifference = currentTime - connectionCache.get(teamname);
- if (timeDifference > 10000) {
- connectionCache.set(teamname, currentTime);
- await updateLastRequestTimestampForTeam(teamname);
- }
- } else {
- await updateLastRequestTimestampForTeam(teamname);
- connectionCache.set(teamname, currentTime);
- }
- } catch (error) {
- logger.warn(`Failed to update lastRequest timestamp for team '${teamname}'"`);
- logger.warn(error.message);
- logger.warn(JSON.stringify(error));
- }
- next();
+// ConfigMap Path (adjust if needed)
+const configMapPath = '/etc/proxy-config/proxy-config.yaml';
+
+// Load ConfigMap data
+let websocketConfig, proxyConfig;
+try {
+ const configFile = fs.readFileSync(path.join(__dirname, '..', '..', configMapPath), 'utf8');
+ const configData = yaml.load(configFile);
+ websocketConfig = configData.websocket;
+ proxyConfig = configData.proxy;
+ logger.info('Loaded ConfigMap data.');
+} catch (error) {
+ logger.error('Error loading ConfigMap data:', error);
+ process.exit(1);
}
/**
- * @param {import("express").Request} req
- * @param {import("express").Response} res
- * @param {import("express").NextFunction} next
+ * Proxy Traffic Handler
+ * @param {Express.Request} req
+ * @param {Express.Response} res
*/
function proxyTrafficToJuiceShop(req, res) {
const teamname = req.teamname;
- const regex = new RegExp('^[a-z0-9-]+$');
- if (!regex.test(teamname)) {
- logger.info(`Got malformed teamname: ${teamname}s`);
- return res.redirect('/balancer/');
- }
- const currentReferrerForDesktop = '/?desktop';
- logger.debug(
- `Proxying request ${req.method.toLocaleUpperCase()} ${
- req.path
- } with matcher for referer: ${currentReferrerForDesktop}`
- );
- let target;
- if (
- (req.query != null && req.query.desktop != null) ||
- (req.headers['referer'] !== undefined &&
- req.headers['referer'].includes(currentReferrerForDesktop)) ||
- (req.headers['Referer'] !== undefined &&
- req.headers['Referer'].includes(currentReferrerForDesktop)) ||
- req.path === '/js/filebrowser.js' ||
- req.path === '/css/filebrowser.css' ||
- req.path === '/files/socket.io/socket.io.js' ||
- req.path === '/js/vendor/jquery.min.js' ||
- req.path === '/files/socket.io/' ||
- req.path === '/files/socket.io/socket.io.js.map'
- ) {
- target = {
- target: `http://${teamname}-virtualdesktop.${teamname}.svc:8080`,
- ws: true,
- };
- } else {
- target = {
- target: `http://${teamname}-wrongsecrets.${teamname}.svc:8080`,
- ws: true,
- };
- }
- logger.info(`we got ${teamname} requesting ${target.target}`);
-
- if (req.path === '/guaclite') {
- let server = res.socket.server;
- logger.info('putting ws through for /quaclite');
- server.on('upgrade', function (req, socket, head) {
- cookieParser(get('cookieParser.secret'))(req, null, () => {});
-
- // logger.info(
- // `we have cookies: ${JSON.stringify(req.cookies)} and ${JSON.stringify(req.signedCookies)}`
- // );
- const upgradeTeamname = extractTeamName(req);
- const regex = new RegExp('^[a-z0-9]([-a-z0-9])+[a-z0-9]$', 'i');
- if (!regex.test(upgradeTeamname)) {
- logger.info(`Got malformed teamname: ${upgradeTeamname}s`);
- return res.redirect('/balancer/');
- }
- logger.info(`proxying upgrade request for: ${req.url} with team ${upgradeTeamname}`);
- proxy.ws(req, socket, head, {
- target: `ws://${upgradeTeamname}-virtualdesktop.${upgradeTeamname}.svc:8080`,
+ const url = req.url;
+
+ const websocketTarget = getWebSocketTarget(teamname, url, websocketConfig);
+ const proxyTarget = getProxyTarget(teamname, url, proxyConfig);
+
+ if (websocketTarget) {
+ logger.info(`Proxying WebSocket request to ${websocketTarget.target}`);
+ proxy.ws(
+ req,
+ res,
+ {
+ target: websocketTarget.target,
ws: true,
- });
- });
- server.on('connect', function (req, socket, head) {
- const connectTeamname = extractTeamName(req);
- const regex = new RegExp('^[a-z0-9]([-a-z0-9])+[a-z0-9]$', 'i');
- if (!regex.test(connectTeamname)) {
- logger.info(`Got malformed teamname: ${teamname}s`);
- return res.redirect('/balancer/');
}
- logger.info(`proxying upgrade request for: ${req.url} with team ${connectTeamname}`);
- proxy.ws(req, socket, head, {
- target: `ws://${connectTeamname}-virtualdesktop.${connectTeamname}.svc:8080`,
- ws: true,
- });
- });
- } else {
- proxy.web(req, res, target, (error) => {
- logger.warn(`Proxy fail '${error.code}' for: ${req.method.toLocaleUpperCase()} ${req.path}`);
-
+ );
+ } else if (proxyTarget) {
+ logger.info(`Proxying request to ${proxyTarget.target}`);
+ proxy.web(
+ req,
+ res,
+ {
+ target: proxyTarget.target,
+ ws: true,
+ },
+ (error) => {
+ logger.warn(
+ `Proxy fail '${error.code}' for: ${req.method.toLocaleUpperCase()} ${req.path}`
+ );
if (error.code !== 'ENOTFOUND' && error.code !== 'EHOSTUNREACH') {
logger.error(error.message);
} else {
logger.debug(error.message);
}
- });
+ }
+ );
+
+ } else {
+ logger.warn(`No proxy target found for ${teamname} and URL ${url}`);
+ res.status(404).send('Not Found');
}
}
-router.use(
- redirectJuiceShopTrafficWithoutBalancerCookies,
- redirectAdminTrafficToBalancerPage,
- checkIfInstanceIsUp,
- updateLastConnectTimestamp,
- proxyTrafficToJuiceShop
-);
+// Helper functions (unchanged)
+function getWebSocketTarget(teamname, url, websocketConfig) {
+ // Implementation
+}
+
+function getProxyTarget(teamname, url, proxyConfig) {
+ // Implementation
+}
+
+router.use(proxyTrafficToJuiceShop);
module.exports = router;