Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions build/dockerfiles/assembly.sshd.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ RUN cp /etc/ssh/sshd_config /sshd-staging/
# Add script to start and stop the service
COPY --chown=0:0 /build/scripts/sshd.init /build/scripts/sshd.start /sshd-staging/

RUN mkdir /opt/www
COPY /build/scripts/code-sshd-page/* /opt/www/
RUN mkdir -p /opt/www/code /opt/www/jetbrains

COPY /build/scripts/code-sshd-page/* /opt/www/code
COPY /build/scripts/jetbrains-sshd-page/* /opt/www/jetbrains

# Lock down /etc/passwd until fixed in UDI
RUN chmod 644 /etc/passwd
Expand Down
79 changes: 79 additions & 0 deletions build/scripts/jetbrains-sshd-page/page-style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright (c) 2026 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

body {
padding-top: 300px;
background-color: #d8d8d8;
color: #444;
font-family: sans-serif;
}
h1 {
font-weight: bold;
text-align: center;
}
a {
color: #0465d7;
}
.center {
text-align: center;
}
.border {
border-style: solid;
padding: 5px;
margin: auto;
width: fit-content;
}



/* Container for code block & clipboard icon */
.parent {
background-color: #f6f8fa;
display: table;
width: 100%;
}

/* Container for clipboard icon of code block */
.clipboard {
background-color: #f6f8fa;
display: table-cell;
width: 2%;
}

.clipboard-img-pre {
width: 15px;
height: 15px;
border: 2px solid black;
border-radius: 5px;
padding: 3px;
}

.clipboard-img-code {
background-color: #e3e6e8;
width: 10px;
height: 10px;
border: 1px solid black;
border-radius: 5px;
padding: 3px;
vertical-align: bottom;
}


/* Colour change for clipboard icon hover */
.clipboard-img-pre:hover {
background:#e3e6e8;
}

.clipboard-img-code:hover {
background-color: #f6f8fa;
}

26 changes: 26 additions & 0 deletions build/scripts/jetbrains-sshd-page/page-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright (c) 2026 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0
*/

function copyToClipboard(id) {
var copyText = document.getElementById(id);
navigator.clipboard.writeText(copyText.innerHTML);
}

function initializePlatformContent() {
if (navigator.userAgent.indexOf('Windows') !== -1) {
var pathEntries = document.getElementsByClassName('path');
for (var i = 0; i < pathEntries.length; i++) {
var currText = pathEntries[i].innerHTML;
currText = currText.replaceAll("/dev/null", "nul");
currText = currText.replaceAll("$HOME", "%USERPROFILE%");
currText = currText.replaceAll("/","\\");
pathEntries[i].innerHTML = currText;
}
}
}
136 changes: 136 additions & 0 deletions build/scripts/jetbrains-sshd-page/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
Copyright (c) 2026 Red Hat, Inc.
This program and the accompanying materials are made
available under the terms of the Eclipse Public License 2.0
which is available at https://www.eclipse.org/legal/epl-2.0/

SPDX-License-Identifier: EPL-2.0
*/

const http = require('http');
const fs = require('fs');

const hostname = '127.0.0.1';
const port = 3400;

let username = "UNKNOWN";
try {
username = fs.readFileSync(`/sshd/username`, 'utf8');
} catch (error) {
// continue
}

const server = http.createServer((req, res) => {
if (req.url === '/') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html');

let hasUserPrefSSHKey = fs.existsSync('/etc/ssh/dwo_ssh_key.pub');

let pubKey = "PUBLIC KEY COULD NOT BE DISPLAYED";
try {
pubKey = fs.readFileSync('/etc/ssh/dwo_ssh_key.pub', 'utf8');
} catch (err) {
// continue
}

let genKey = "PRIVATE KEY NOT FOUND";
try {
genKey = fs.readFileSync(`/sshd/ssh_client_ed25519_key`, 'utf8');
} catch (err) {
// continue
}

let keyMessage = hasUserPrefSSHKey ? pubKey : genKey;

res.end(`
<!DOCTYPE html>
<html>
<head>
<title>${process.env["DEVWORKSPACE_NAME"]}</title>
<link rel="stylesheet" href="page-style.css">
<script src="page-utils.js"></script>
</head>
<body>
<script>
(function () {
window.onload = function () {
openToolbox()
}
}())

function openToolbox() {
const tbxLink = "jetbrains://gateway/com.jetbrains.toolbox.sample?key=${encodeURIComponent(keyMessage)}&dwName=${process.env['DEVWORKSPACE_NAME']}"
console.log("Opening Toolbox app: " + tbxLink);
window.open(tbxLink, "_self");
}
</script>

<h1>Workspace ${process.env["DEVWORKSPACE_NAME"]} is running</h1>


<div class="border">
<h4 class="center">Make sure your local <a href="${process.env["CLUSTER_CONSOLE_URL"]}/command-line-tools" target="_blank">oc client</a> is <a href="https://oauth-openshift${getHostURL()}/oauth/token/request" target="_blank">logged in</a> to your OpenShift cluster</h4>
<p class="center">Run <code id="port-forward">oc port-forward -n ${process.env["DEVWORKSPACE_NAMESPACE"]} ${process.env["HOSTNAME"]} 2022:2022</code><a href="#"><svg class="clipboard-img-code" onclick="copyToClipboard('port-forward')" title="Copy" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 20 20">
<path fill="currentColor" d="M12 0H2C.9 0 0 .9 0 2v10h1V2c0-.6.4-1 1-1h10V0z"></path>
<path fill="currentColor" d="M18 20H8c-1.1 0-2-.9-2-2V8c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2v10c0 1.1-.9 2-2 2zM8 7c-.6 0-1 .4-1 1v10c0 .6.4 1 1 1h10c.6 0 1-.4 1-1V8c0-.6-.4-1-1-1H8z"></path>
</svg></a>. This establishes a connection to the workspace.</p>
</div>

<h4 class="center">Can't open the workspace?</h4>
<p class="center">If your browser doesn't ask you to open Toolbox, make sure the prerequisites mentioned in <a href="https://docs.redhat.com/en/documentation/red_hat_openshift_dev_spaces/latest/html/user_guide/ides-in-workspaces#idea-ultimate" target="_blank">the documentation</a> are met.</p>

<!-- Provide an alternative way to open IDE, in case the browser can't show a pop-up -->
<p class="center"><a href="javascript:;" onclick="openToolbox()"><b>Open the workspace over Toolbox</b></a></p>
<p class="center"><a href="${process.env["CHE_DASHBOARD_URL"]}" target="_blank">Open Dashboard</a></p>

<script>initializePlatformContent();</script>
</body>
</html>
`);
} else {
let loc = req.url.substring(1);
let isBinaryData = false;
let content = "";

res.statusCode = 200;
if (loc.endsWith(".css")) {
res.setHeader("Content-Type", "text/css");
} else if (loc.endsWith(".js")) {
res.setHeader("Content-Type", "text/javascript");
} else if (loc.endsWith(".png")) {
res.setHeader("Content-Type", "image/png");
isBinaryData = true;
} else {
res.setHeader("Content-Type", "text/plain");
}

try {
content = fs.readFileSync(loc, isBinaryData ? null : "utf8");
} catch (err) {
// continue
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain");
content = "Not Found";
}
res.end(content);
}
});

server.listen(port, hostname, () => {
console.log(`Server is running at http://${hostname}:${port}/`);
});

function getHostURL () {
const consoleURL = process.env["CLUSTER_CONSOLE_URL"];
const devspacesURL = process.env["CHE_DASHBOARD_URL"];
if (consoleURL === undefined || devspacesURL === undefined) {
return undefined;
}
let i = 0;
while (i < consoleURL.length && i < devspacesURL.length
&& consoleURL.substring(consoleURL.length - 1 - i) === devspacesURL.substring(devspacesURL.length - 1 - i)) {
i++;
}
return consoleURL.substring(consoleURL.length - i);
}
9 changes: 7 additions & 2 deletions build/scripts/sshd.init
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ while [ ! -e /sshd/username ]; do
done

# start the landing page
pushd /opt/www/
exec node /opt/www/server.js
ide_provider="$1"
if [ "$ide_provider" = "jetbrains" ]; then
pushd /opt/www/jetbrains
else # default to landing page for Code
pushd /opt/www/code
fi

exec node server.js
Loading