Skip to content

Commit ff8788f

Browse files
committed
Improve the connection instructions landing page for VS Code (SSHD)
- Add CSS styling for code blocks, as well as a copy feature - Specify the namespace for oc port-forward (-n) - Move private key permission warning to listing of the key - Move instructions about "VS Code" SSH extension - Make it more clear the example path to private key should be replaced - Link to the "Command Line Tools" page and the "Token Request" page Signed-off-by: Roland Grunberg <rgrunber@redhat.com>
1 parent 335a0d8 commit ff8788f

File tree

5 files changed

+234
-81
lines changed

5 files changed

+234
-81
lines changed

build/dockerfiles/dev.sshd.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ RUN sed -i \
4848
COPY --chown=0:0 /build/scripts/sshd.start /
4949

5050
RUN mkdir /opt/www
51-
COPY /build/scripts/server.js /opt/www/
51+
COPY /build/scripts/code-sshd-page/* /opt/www/
5252

5353
# Lock down /etc/passwd until fixed in UDI
5454
RUN chmod 644 /etc/passwd
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright (c) 2025 Red Hat, Inc.
3+
This program and the accompanying materials are made
4+
available under the terms of the Eclipse Public License 2.0
5+
which is available at https://www.eclipse.org/legal/epl-2.0/
6+
7+
SPDX-License-Identifier: EPL-2.0
8+
*/
9+
10+
pre {
11+
background-color: #f6f8fa; /* Light gray background */
12+
overflow-x: auto; /* Enable horizontal scrolling for long lines */
13+
font-family: monospace;
14+
font-size: 14px;
15+
display: table-cell;
16+
width: 98%;
17+
padding: 10px;
18+
}
19+
20+
code {
21+
background-color: #e3e6e8; /* Slightly darker gray for inline code */
22+
padding: 2px 4px;
23+
border-radius: 3px;
24+
font-family: monospace;
25+
font-size: 0.9em; /* Slightly smaller than surrounding text */
26+
}
27+
28+
.parent {
29+
background-color: #f6f8fa; /* Light gray background */
30+
display: table;
31+
width: 100%;
32+
}
33+
34+
.clipboard {
35+
background-color: #f6f8fa; /* Light gray background */
36+
display: table-cell;
37+
width: 2%;
38+
}
39+
40+
.clipboard-img-pre {
41+
width: 15px;
42+
height: 15px;
43+
border: 2px solid black;
44+
border-radius: 5px;
45+
padding: 3px;
46+
}
47+
48+
.clipboard-img-code {
49+
background-color: #e3e6e8; /* Slightly darker gray for inline code */
50+
width: 10px;
51+
height: 10px;
52+
border: 1px solid black;
53+
border-radius: 5px;
54+
padding: 3px;
55+
vertical-align: bottom;
56+
}
57+
58+
.clipboard-img-pre:hover {
59+
background:#e3e6e8;
60+
}
61+
62+
.clipboard-img-code:hover {
63+
background-color: #f6f8fa;
64+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*
2+
Copyright (c) 2025 Red Hat, Inc.
3+
This program and the accompanying materials are made
4+
available under the terms of the Eclipse Public License 2.0
5+
which is available at https://www.eclipse.org/legal/epl-2.0/
6+
7+
SPDX-License-Identifier: EPL-2.0
8+
*/
9+
10+
function copyToClipboard(id) {
11+
var copyText = document.getElementById(id);
12+
navigator.clipboard.writeText(copyText.innerHTML);
13+
}
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
Copyright (c) 2025 Red Hat, Inc.
3+
This program and the accompanying materials are made
4+
available under the terms of the Eclipse Public License 2.0
5+
which is available at https://www.eclipse.org/legal/epl-2.0/
6+
7+
SPDX-License-Identifier: EPL-2.0
8+
*/
9+
10+
const http = require('http');
11+
const fs = require('fs');
12+
const os = require('os');
13+
14+
const hostname = '127.0.0.1';
15+
const port = 3400;
16+
17+
const server = http.createServer((req, res) => {
18+
if (req.url === '/') {
19+
res.statusCode = 200;
20+
res.setHeader('Content-Type', 'text/html');
21+
22+
let hasUserPrefSSHKey = fs.existsSync('/etc/ssh/dwo_ssh_key.pub');
23+
24+
let pubKey = "PUBLIC KEY COULD NOT BE DISPLAYED";
25+
try {
26+
pubKey = fs.readFileSync('/etc/ssh/dwo_ssh_key.pub', 'utf8');
27+
} catch (err) {
28+
// continue
29+
}
30+
31+
let genKey = "PRIVATE KEY NOT FOUND";
32+
try {
33+
genKey = fs.readFileSync(`${process.env["HOME"]}/.ssh/ssh_client_ed25519_key`, 'utf8');
34+
} catch (err) {
35+
// continue
36+
}
37+
38+
let keyMessage = hasUserPrefSSHKey ? pubKey : genKey;
39+
40+
res.end(`
41+
<!DOCTYPE html>
42+
<html>
43+
<head>
44+
<title>${process.env["DEVWORKSPACE_NAME"]}</title>
45+
<link rel="stylesheet" href="page-style.css">
46+
<script src="page-utils.js"></script>
47+
</head>
48+
<body>
49+
<h1>Workspace ${process.env["DEVWORKSPACE_NAME"]} is running</h1>
50+
<div class="border">
51+
<ol>
52+
<li>Make sure your local <a href="${process.env["CLUSTER_CONSOLE_URL"]}/command-line-tools">oc client</a> is <a href="https://oauth-openshift${getHostURL()}/oauth/token/request">logged in</a> to your OpenShift cluster</li>
53+
<li><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">
54+
<path fill="currentColor" d="M12 0H2C.9 0 0 .9 0 2v10h1V2c0-.6.4-1 1-1h10V0z"></path>
55+
<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>
56+
</svg></a>. This establishes a connection to the workspace.</p></li>
57+
<li>
58+
In your local VS Code instance, with either <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh">"Remote - SSH"</a> (for VS Code), or <a href="https://open-vsx.org/extension/jeanp413/open-remote-ssh">"Open Remote - SSH"</a> (for Code-OSS), connect to <code>localhost</code> on port <code>2022</code> with user <code>${os.userInfo().username}</code> ${hasUserPrefSSHKey ? `. The SSH key, corresponding to the following public key, configured in the "SSH Keys" tab of "User Preferences" has been authorized to connect :` : `and the following identity file :`}
59+
<div class="parent">
60+
<div>
61+
<pre id="key">${keyMessage}</pre>
62+
</div>
63+
<div class="clipboard">
64+
<a href="#">
65+
<svg class="clipboard-img-pre" onclick="copyToClipboard('key') title="Copy" "xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 20 20">
66+
<path fill="currentColor" d="M12 0H2C.9 0 0 .9 0 2v10h1V2c0-.6.4-1 1-1h10V0z"></path>
67+
<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>
68+
</svg>
69+
</a>
70+
</div>
71+
</div>
72+
<p>
73+
<b>&#9888; Please ensure the permissions on the private key used are restricted to allow only the file owner to read/write. The SSH service may fail to correctly authenticate otherwise.</b>
74+
</p>
75+
This can also be configured locally in <code>$HOME/.ssh/config</code> with the following :
76+
<div class="parent">
77+
<div>
78+
<pre id="config">Host localhost
79+
HostName 127.0.0.1
80+
User ${os.userInfo().username}
81+
Port 2022
82+
IdentityFile /path/to/the/ssh_client_ed25519_key
83+
UserKnownHostsFile /dev/null</pre>
84+
</div>
85+
<div class="clipboard">
86+
<a href="#">
87+
<svg class="clipboard-img-pre" onclick="copyToClipboard('config') title="Copy" "xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 20 20">
88+
<path fill="currentColor" d="M12 0H2C.9 0 0 .9 0 2v10h1V2c0-.6.4-1 1-1h10V0z"></path>
89+
<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>
90+
</svg>
91+
</a>
92+
</div>
93+
</div>
94+
<p>
95+
Where <code>/path/to/the/ssh_client_ed25519_key</code> should be replaced by the absolute path to the private key file on your local system.
96+
</p>
97+
</li>
98+
</ol>
99+
<h3>Troubleshooting</h3>
100+
<p>If the connection fails with "<code>WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED</code>", it may be necessary to remove the <code>localhost</code> or <code>127.0.0.1</code> entries from <code>$HOME/.ssh/known_hosts</code>. This is because the SSHD service container (to which <code>oc port-forward</code> is forwarding) may change. This can be bypassed by setting <code>UserKnownHostsFile /dev/null</code></p>
101+
<p>If the connection fails for an unknown reason, consider disabling the setting <code>remote.SSH.useExecServer</code> (set to false)</p>
102+
<p>For any other issues, relating to the use of a VS Code-based editor and the "Remote - SSH", the "Remote - SSH" logs from the "Output" view are very helpful in diagnosing the issue.</p>
103+
</div>
104+
</body>
105+
</html>
106+
`);
107+
} else {
108+
let loc = req.url.substring(1);
109+
let isBinaryData = false;
110+
let content = "";
111+
112+
res.statusCode = 200;
113+
if (loc.endsWith(".css")) {
114+
res.setHeader("Content-Type", "text/css");
115+
} else if (loc.endsWith(".js")) {
116+
res.setHeader("Content-Type", "text/javascript");
117+
} else if (loc.endsWith(".png")) {
118+
res.setHeader("Content-Type", "image/png");
119+
isBinaryData = true;
120+
} else {
121+
res.setHeader("Content-Type", "text/plain");
122+
}
123+
124+
try {
125+
content = fs.readFileSync(loc, isBinaryData ? null : "utf8");
126+
} catch (err) {
127+
// continue
128+
res.statusCode = 404;
129+
res.setHeader("Content-Type", "text/plain");
130+
content = "Not Found";
131+
}
132+
res.end(content);
133+
}
134+
});
135+
136+
server.listen(port, hostname, () => {
137+
console.log(`Server running at http://${hostname}:${port}/`);
138+
});
139+
140+
function getHostURL () {
141+
const consoleURL = process.env["CLUSTER_CONSOLE_URL"];
142+
const devspacesURL = process.env["CHE_DASHBOARD_URL"];
143+
if (consoleURL === undefined || devspacesURL === undefined) {
144+
return undefined;
145+
}
146+
let i = 0;
147+
while (i < consoleURL.length || i < devspacesURL.length) {
148+
if (consoleURL.substring(consoleURL.length - 1 - i) != devspacesURL.substring(devspacesURL.length - 1 - i)) {
149+
if (i != 0) {
150+
break;
151+
}
152+
}
153+
i++;
154+
}
155+
return consoleURL.substring(consoleURL.length - i);
156+
}

build/scripts/server.js

Lines changed: 0 additions & 80 deletions
This file was deleted.

0 commit comments

Comments
 (0)