Skip to content

Commit cf78db3

Browse files
Copilotphrocker
andcommitted
Integrate SSH proxy with existing HostSystem database configuration
Co-authored-by: phrocker <[email protected]>
1 parent eb9abab commit cf78db3

File tree

5 files changed

+246
-20
lines changed

5 files changed

+246
-20
lines changed

sentrius-chart/templates/ssh-proxy-deployment.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ spec:
2929
value: "{{ .Values.sshproxy.enabled }}"
3030
- name: SENTRIUS_SSH_PROXY_PORT
3131
value: "{{ .Values.sshproxy.port }}"
32-
- name: SENTRIUS_SSH_PROXY_TARGET_SSH_DEFAULT_HOST
33-
value: "{{ .Values.sshproxy.targetSsh.defaultHost }}"
34-
- name: SENTRIUS_SSH_PROXY_TARGET_SSH_DEFAULT_PORT
35-
value: "{{ .Values.sshproxy.targetSsh.defaultPort }}"
32+
- name: SENTRIUS_SSH_PROXY_CONNECTION_CONNECTION_TIMEOUT
33+
value: "{{ .Values.sshproxy.connection.connectionTimeout }}"
34+
- name: SENTRIUS_SSH_PROXY_CONNECTION_KEEP_ALIVE_INTERVAL
35+
value: "{{ .Values.sshproxy.connection.keepAliveInterval }}"
36+
- name: SENTRIUS_SSH_PROXY_CONNECTION_MAX_RETRIES
37+
value: "{{ .Values.sshproxy.connection.maxRetries }}"
3638
resources:
3739
{{- toYaml .Values.sshproxy.resources | nindent 12 }}
3840
livenessProbe:

sentrius-chart/values.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@ sshproxy:
378378
serviceType: ClusterIP
379379
nodePort: 30022 # Only used if serviceType is NodePort
380380
resources: {}
381-
targetSsh:
382-
defaultHost: "localhost"
383-
defaultPort: 22
381+
connection:
382+
# Connection settings for target SSH servers from database
384383
connectionTimeout: 30000
385-
keepAliveInterval: 60000
384+
keepAliveInterval: 60000
385+
maxRetries: 3

ssh-proxy/src/main/java/io/sentrius/sso/sshproxy/config/SshProxyConfig.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ public class SshProxyConfig {
1414
private boolean enabled = true;
1515
private int maxConcurrentSessions = 100;
1616

17-
// Target SSH configuration
18-
private TargetSsh targetSsh = new TargetSsh();
17+
// Connection settings for target SSH servers
18+
private Connection connection = new Connection();
1919

2020
@Data
21-
public static class TargetSsh {
22-
private String defaultHost = "localhost";
23-
private int defaultPort = 22;
21+
public static class Connection {
2422
private int connectionTimeout = 30000;
2523
private int keepAliveInterval = 60000;
24+
private int maxRetries = 3;
2625
}
2726
}

ssh-proxy/src/main/java/io/sentrius/sso/sshproxy/handler/SshProxyShellHandler.java

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
import io.sentrius.sso.automation.auditing.Trigger;
44
import io.sentrius.sso.automation.auditing.TriggerAction;
55
import io.sentrius.sso.core.model.ConnectedSystem;
6+
import io.sentrius.sso.core.model.HostSystem;
67
import io.sentrius.sso.core.services.terminal.SessionTrackingService;
78
import io.sentrius.sso.sshproxy.config.SshProxyConfig;
9+
import io.sentrius.sso.sshproxy.service.HostSystemSelectionService;
810
import io.sentrius.sso.sshproxy.service.InlineTerminalResponseService;
911
import io.sentrius.sso.sshproxy.service.SshCommandProcessor;
1012
import lombok.RequiredArgsConstructor;
@@ -36,6 +38,7 @@ public class SshProxyShellHandler implements Factory<Command> {
3638
private final SessionTrackingService sessionTrackingService;
3739
private final SshCommandProcessor commandProcessor;
3840
private final InlineTerminalResponseService terminalResponseService;
41+
private final HostSystemSelectionService hostSystemSelectionService;
3942
private final SshProxyConfig config;
4043

4144
// Track active sessions
@@ -58,6 +61,7 @@ private class SshProxyShell implements Command {
5861
private Environment environment;
5962
private ServerSession session;
6063
private ConnectedSystem connectedSystem;
64+
private HostSystem selectedHostSystem;
6165
private Thread shellThread;
6266
private volatile boolean running = false;
6367

@@ -94,6 +98,7 @@ public void start(ChannelSession channel, Environment env) throws IOException {
9498
// Initialize Sentrius session tracking
9599
try {
96100
initializeSentriusSession(username, sessionId);
101+
initializeHostSystemSelection();
97102
sendWelcomeMessage();
98103
startShellLoop();
99104
} catch (Exception e) {
@@ -116,7 +121,26 @@ private void initializeSentriusSession(String username, String sessionId) {
116121
log.info("Initialized Sentrius session for user: {}", username);
117122
}
118123

124+
private void initializeHostSystemSelection() {
125+
// Try to get a default HostSystem from the database
126+
selectedHostSystem = hostSystemSelectionService.getDefaultHostSystem().orElse(null);
127+
128+
if (selectedHostSystem == null || !hostSystemSelectionService.isHostSystemValid(selectedHostSystem)) {
129+
log.warn("No valid HostSystem found for SSH proxy session");
130+
} else {
131+
log.info("Selected HostSystem: {} ({}:{})",
132+
selectedHostSystem.getDisplayName(),
133+
selectedHostSystem.getHost(),
134+
selectedHostSystem.getPort());
135+
}
136+
}
137+
119138
private void sendWelcomeMessage() throws IOException {
139+
String hostInfo = selectedHostSystem != null
140+
? String.format("%s (%s:%d)", selectedHostSystem.getDisplayName(),
141+
selectedHostSystem.getHost(), selectedHostSystem.getPort())
142+
: "No target host configured";
143+
120144
String welcome = "\r\n" +
121145
"╔══════════════════════════════════════════════════════════════╗\r\n" +
122146
"║ SENTRIUS SSH PROXY ║\r\n" +
@@ -125,14 +149,17 @@ private void sendWelcomeMessage() throws IOException {
125149
"\r\n" +
126150
"Welcome! This SSH session is protected by Sentrius safeguards.\r\n" +
127151
"All commands are monitored and may be blocked based on security policies.\r\n" +
152+
"\r\n" +
153+
"Target Host: " + hostInfo + "\r\n" +
128154
"\r\n";
129155

130156
terminalResponseService.sendMessage(welcome, out);
131157
sendPrompt();
132158
}
133159

134160
private void sendPrompt() throws IOException {
135-
String prompt = String.format("[sentrius@%s]$ ", config.getTargetSsh().getDefaultHost());
161+
String hostname = selectedHostSystem != null ? selectedHostSystem.getHost() : "unknown";
162+
String prompt = String.format("[sentrius@%s]$ ", hostname);
136163
terminalResponseService.sendMessage(prompt, out);
137164
}
138165

@@ -214,6 +241,7 @@ private void processCommand(String command) throws IOException {
214241

215242
private boolean handleBuiltinCommand(String command) throws IOException {
216243
String cmd = command.toLowerCase().trim();
244+
String[] parts = command.trim().split("\\s+");
217245

218246
switch (cmd) {
219247
case "exit":
@@ -231,17 +259,27 @@ private boolean handleBuiltinCommand(String command) throws IOException {
231259
showStatus();
232260
return true;
233261

262+
case "hosts":
263+
showAvailableHosts();
264+
return true;
265+
234266
default:
267+
if (parts.length >= 2 && "connect".equals(parts[0].toLowerCase())) {
268+
return handleConnectCommand(parts);
269+
}
235270
return false;
236271
}
237272
}
238273

239274
private void showHelp() throws IOException {
240275
String help = "\r\n" +
241276
"Sentrius SSH Proxy - Built-in Commands:\r\n" +
242-
" help - Show this help message\r\n" +
243-
" status - Show session status\r\n" +
244-
" exit - Close SSH session\r\n" +
277+
" help - Show this help message\r\n" +
278+
" status - Show session status\r\n" +
279+
" hosts - List available target hosts\r\n" +
280+
" connect <id> - Connect to HostSystem by ID\r\n" +
281+
" connect <name> - Connect to HostSystem by display name\r\n" +
282+
" exit - Close SSH session\r\n" +
245283
"\r\n" +
246284
"All other commands are forwarded to the target SSH server\r\n" +
247285
"and subject to Sentrius security policies.\r\n\r\n";
@@ -250,21 +288,99 @@ private void showHelp() throws IOException {
250288
}
251289

252290
private void showStatus() throws IOException {
291+
String hostInfo = selectedHostSystem != null
292+
? String.format("%s (%s:%d)", selectedHostSystem.getDisplayName(),
293+
selectedHostSystem.getHost(), selectedHostSystem.getPort())
294+
: "No target host configured";
295+
253296
String status = String.format("\r\n" +
254297
"Sentrius SSH Proxy Status:\r\n" +
255298
" User: %s\r\n" +
256-
" Target Host: %s:%d\r\n" +
299+
" Target Host: %s\r\n" +
257300
" Session Active: %s\r\n" +
258301
" Safeguards: ENABLED\r\n\r\n",
259302
session.getUsername(),
260-
config.getTargetSsh().getDefaultHost(),
261-
config.getTargetSsh().getDefaultPort(),
303+
hostInfo,
262304
running ? "YES" : "NO"
263305
);
264306

265307
terminalResponseService.sendMessage(status, out);
266308
}
267309

310+
private void showAvailableHosts() throws IOException {
311+
var hostSystems = hostSystemSelectionService.getAllHostSystems();
312+
313+
StringBuilder hostList = new StringBuilder("\r\nAvailable HostSystems:\r\n");
314+
hostList.append("ID\tName\t\t\tHost:Port\t\tStatus\r\n");
315+
hostList.append("────────────────────────────────────────────────────────────\r\n");
316+
317+
if (hostSystems.isEmpty()) {
318+
hostList.append("No HostSystems configured in database.\r\n");
319+
} else {
320+
for (HostSystem hs : hostSystems) {
321+
String name = hs.getDisplayName() != null ? hs.getDisplayName() : "N/A";
322+
String hostPort = String.format("%s:%d", hs.getHost(), hs.getPort());
323+
String status = hostSystemSelectionService.isHostSystemValid(hs) ? "Valid" : "Invalid";
324+
String current = (selectedHostSystem != null && selectedHostSystem.getId().equals(hs.getId())) ? " *" : "";
325+
326+
hostList.append(String.format("%d\t%-15s\t%-15s\t%s%s\r\n",
327+
hs.getId(), name, hostPort, status, current));
328+
}
329+
hostList.append("\r\n* = Current selection\r\n");
330+
}
331+
hostList.append("\r\n");
332+
333+
terminalResponseService.sendMessage(hostList.toString(), out);
334+
}
335+
336+
private boolean handleConnectCommand(String[] parts) throws IOException {
337+
if (parts.length < 2) {
338+
terminalResponseService.sendMessage("Usage: connect <id|name>\r\n", out);
339+
return true;
340+
}
341+
342+
String target = parts[1];
343+
HostSystem targetHost = null;
344+
345+
// Try to parse as ID first
346+
try {
347+
Long id = Long.parseLong(target);
348+
targetHost = hostSystemSelectionService.getHostSystemById(id).orElse(null);
349+
} catch (NumberFormatException e) {
350+
// Not a number, try by display name
351+
var hostsByName = hostSystemSelectionService.getHostSystemsByDisplayName(target);
352+
if (!hostsByName.isEmpty()) {
353+
targetHost = hostsByName.get(0);
354+
if (hostsByName.size() > 1) {
355+
terminalResponseService.sendMessage(
356+
String.format("Warning: Multiple hosts found with name '%s', using first one.\r\n", target), out);
357+
}
358+
}
359+
}
360+
361+
if (targetHost == null) {
362+
terminalResponseService.sendMessage(
363+
String.format("Error: HostSystem '%s' not found.\r\n", target), out);
364+
return true;
365+
}
366+
367+
if (!hostSystemSelectionService.isHostSystemValid(targetHost)) {
368+
terminalResponseService.sendMessage(
369+
String.format("Error: HostSystem '%s' is not properly configured.\r\n", target), out);
370+
return true;
371+
}
372+
373+
selectedHostSystem = targetHost;
374+
terminalResponseService.sendMessage(
375+
String.format("Connected to HostSystem: %s (%s:%d)\r\n",
376+
targetHost.getDisplayName(), targetHost.getHost(), targetHost.getPort()), out);
377+
378+
log.info("SSH proxy session switched to HostSystem: {} ({}:{})",
379+
targetHost.getDisplayName(), targetHost.getHost(), targetHost.getPort());
380+
381+
return true;
382+
}
383+
268384
private void executeCommand(String command) throws IOException {
269385
// TODO: Implement actual command forwarding to target SSH server
270386
// For now, simulate command execution
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package io.sentrius.sso.sshproxy.service;
2+
3+
import io.sentrius.sso.core.model.HostSystem;
4+
import io.sentrius.sso.core.repository.SystemRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import lombok.extern.slf4j.Slf4j;
7+
import org.springframework.stereotype.Service;
8+
9+
import java.util.List;
10+
import java.util.Optional;
11+
12+
/**
13+
* Service for managing HostSystem selection in SSH proxy sessions.
14+
* Integrates with the existing Sentrius HostSystem database configuration.
15+
*/
16+
@Slf4j
17+
@Service
18+
@RequiredArgsConstructor
19+
public class HostSystemSelectionService {
20+
21+
private final SystemRepository systemRepository;
22+
23+
/**
24+
* Get a HostSystem by ID for SSH proxy connection.
25+
*/
26+
public Optional<HostSystem> getHostSystemById(Long id) {
27+
try {
28+
return systemRepository.findById(id);
29+
} catch (Exception e) {
30+
log.error("Error retrieving HostSystem with ID: {}", id, e);
31+
return Optional.empty();
32+
}
33+
}
34+
35+
/**
36+
* Get all available HostSystems for SSH proxy.
37+
*/
38+
public List<HostSystem> getAllHostSystems() {
39+
try {
40+
return systemRepository.findAll();
41+
} catch (Exception e) {
42+
log.error("Error retrieving all HostSystems", e);
43+
return List.of();
44+
}
45+
}
46+
47+
/**
48+
* Find HostSystems by display name.
49+
*/
50+
public List<HostSystem> getHostSystemsByDisplayName(String displayName) {
51+
try {
52+
return systemRepository.findByDisplayName(displayName);
53+
} catch (Exception e) {
54+
log.error("Error retrieving HostSystems by display name: {}", displayName, e);
55+
return List.of();
56+
}
57+
}
58+
59+
/**
60+
* Find HostSystems by host address.
61+
*/
62+
public List<HostSystem> getHostSystemsByHost(String host) {
63+
try {
64+
return systemRepository.findAll().stream()
65+
.filter(hs -> host.equals(hs.getHost()))
66+
.toList();
67+
} catch (Exception e) {
68+
log.error("Error retrieving HostSystems by host: {}", host, e);
69+
return List.of();
70+
}
71+
}
72+
73+
/**
74+
* Get the default HostSystem (first available one) for SSH proxy.
75+
*/
76+
public Optional<HostSystem> getDefaultHostSystem() {
77+
try {
78+
List<HostSystem> hostSystems = systemRepository.findAll();
79+
if (!hostSystems.isEmpty()) {
80+
HostSystem defaultHost = hostSystems.get(0);
81+
log.info("Using default HostSystem: {} ({}:{})",
82+
defaultHost.getDisplayName(), defaultHost.getHost(), defaultHost.getPort());
83+
return Optional.of(defaultHost);
84+
}
85+
} catch (Exception e) {
86+
log.error("Error retrieving default HostSystem", e);
87+
}
88+
return Optional.empty();
89+
}
90+
91+
/**
92+
* Validate if a HostSystem is available and properly configured for SSH proxy.
93+
*/
94+
public boolean isHostSystemValid(HostSystem hostSystem) {
95+
if (hostSystem == null) {
96+
return false;
97+
}
98+
99+
boolean valid = hostSystem.getHost() != null && !hostSystem.getHost().trim().isEmpty()
100+
&& hostSystem.getPort() != null && hostSystem.getPort() > 0
101+
&& hostSystem.getSshUser() != null && !hostSystem.getSshUser().trim().isEmpty();
102+
103+
if (!valid) {
104+
log.warn("HostSystem {} is not properly configured for SSH proxy", hostSystem.getId());
105+
}
106+
107+
return valid;
108+
}
109+
}

0 commit comments

Comments
 (0)