Skip to content

Commit 4e0a7f8

Browse files
authored
Rdpfactory (#7)
* Implement RDP proxy module with Sentrius integration
1 parent c1510fc commit 4e0a7f8

File tree

28 files changed

+3497
-20
lines changed

28 files changed

+3497
-20
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,5 @@ api/node
6060
.generated/
6161
# Ignore Generated keys if they exist
6262
docker/dev-certs/sentrius-ca.crt
63-
docker/dev-certs/sentrius-ca.key
63+
docker/dev-certs/sentrius-ca.key
64+
docker/*/dev-certs/sentrius-ca.crt

.local.env

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
SENTRIUS_VERSION=1.1.411
2-
SENTRIUS_SSH_VERSION=1.1.41
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.53
4-
SENTRIUS_AGENT_VERSION=1.1.42
5-
SENTRIUS_AI_AGENT_VERSION=1.1.284
6-
LLMPROXY_VERSION=1.0.84
7-
LAUNCHER_VERSION=1.0.88
8-
AGENTPROXY_VERSION=1.0.85
9-
SSHPROXY_VERSION=1.0.88
1+
SENTRIUS_VERSION=1.1.413
2+
SENTRIUS_SSH_VERSION=1.1.43
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.55
4+
SENTRIUS_AGENT_VERSION=1.1.43
5+
SENTRIUS_AI_AGENT_VERSION=1.1.285
6+
LLMPROXY_VERSION=1.0.85
7+
LAUNCHER_VERSION=1.0.89
8+
AGENTPROXY_VERSION=1.0.86
9+
SSHPROXY_VERSION=1.0.89
10+
RDPPROXY_VERSION=1.0.2

.local.env.bak

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
SENTRIUS_VERSION=1.1.411
2-
SENTRIUS_SSH_VERSION=1.1.41
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.53
4-
SENTRIUS_AGENT_VERSION=1.1.42
5-
SENTRIUS_AI_AGENT_VERSION=1.1.284
6-
LLMPROXY_VERSION=1.0.84
7-
LAUNCHER_VERSION=1.0.88
8-
AGENTPROXY_VERSION=1.0.85
9-
SSHPROXY_VERSION=1.0.88
1+
SENTRIUS_VERSION=1.1.413
2+
SENTRIUS_SSH_VERSION=1.1.43
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.55
4+
SENTRIUS_AGENT_VERSION=1.1.43
5+
SENTRIUS_AI_AGENT_VERSION=1.1.285
6+
LLMPROXY_VERSION=1.0.85
7+
LAUNCHER_VERSION=1.0.89
8+
AGENTPROXY_VERSION=1.0.86
9+
SSHPROXY_VERSION=1.0.89
10+
RDPPROXY_VERSION=1.0.2

dataplane/src/main/java/io/sentrius/sso/core/model/HostSystem.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,26 @@ public class HostSystem implements Host {
101101
@Column(name = "proxied_ssh_port")
102102
private Integer proxiedSSHPort = 0;
103103

104+
// RDP Support fields
105+
@Builder.Default
106+
@Column(name = "rdp_enabled")
107+
private boolean rdpEnabled = false;
108+
109+
@Builder.Default
110+
@Column(name = "rdp_user")
111+
private String rdpUser = "Administrator";
112+
113+
@Builder.Default
114+
@Column(name = "rdp_password")
115+
private String rdpPassword = "";
116+
117+
@Builder.Default
118+
@Column(name = "rdp_port")
119+
private Integer rdpPort = 3389;
104120

121+
@Builder.Default
122+
@Column(name = "rdp_domain")
123+
private String rdpDomain = "";
105124

106125
@OneToMany(mappedBy = "hostSystem", cascade = CascadeType.ALL,orphanRemoval = true, fetch = FetchType.LAZY)
107126
private List<ProxyHost> proxies;
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package io.sentrius.sso.core.services;
2+
3+
import io.sentrius.sso.automation.auditing.Trigger;
4+
import io.sentrius.sso.automation.auditing.TriggerAction;
5+
import io.sentrius.sso.core.integrations.ssh.DataSession;
6+
import io.sentrius.sso.core.model.ConnectedSystem;
7+
import io.sentrius.sso.core.services.security.CryptoService;
8+
import io.sentrius.sso.core.services.terminal.SessionTrackingService;
9+
import io.sentrius.sso.protobuf.Session;
10+
import lombok.RequiredArgsConstructor;
11+
import lombok.extern.slf4j.Slf4j;
12+
import org.springframework.beans.factory.annotation.Qualifier;
13+
import org.springframework.scheduling.annotation.Async;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.web.socket.TextMessage;
16+
17+
import java.io.IOException;
18+
import java.security.GeneralSecurityException;
19+
import java.util.Base64;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ConcurrentMap;
24+
import java.util.concurrent.Executor;
25+
import java.util.concurrent.TimeUnit;
26+
27+
/**
28+
* RDP listener service that monitors and manages RDP sessions.
29+
* Similar to SshListenerService but adapted for RDP protocol.
30+
*/
31+
@Slf4j
32+
@Service
33+
@RequiredArgsConstructor
34+
public class RdpListenerService {
35+
36+
private final SessionTrackingService sessionTrackingService;
37+
private final CryptoService cryptoService;
38+
39+
@Qualifier("rdpTaskExecutor") // Use RDP-specific executor
40+
private final Executor taskExecutor;
41+
42+
private final ConcurrentMap<String, DataSession> activeSessions = new ConcurrentHashMap<>();
43+
44+
public void startAuditingSession(String terminalSessionId, DataSession session) throws GeneralSecurityException {
45+
var sessionIdStr = cryptoService.decrypt(terminalSessionId);
46+
var sessionIdLong = Long.parseLong(sessionIdStr);
47+
48+
log.info("Starting to audit RDP session: {}", terminalSessionId);
49+
activeSessions.putIfAbsent(terminalSessionId, session);
50+
}
51+
52+
public void endAuditingSession(String terminalSessionId) throws GeneralSecurityException {
53+
log.info("Ending RDP audit session: {}", terminalSessionId);
54+
activeSessions.remove(terminalSessionId);
55+
}
56+
57+
public void startListeningToRdpServer(String terminalSessionId, DataSession session) throws GeneralSecurityException {
58+
var sessionIdStr = cryptoService.decrypt(terminalSessionId);
59+
var sessionIdLong = Long.parseLong(sessionIdStr);
60+
61+
var connectedSystem = sessionTrackingService.getConnectedSession(sessionIdLong);
62+
63+
log.info("Starting to listen to RDP server for session: {}", terminalSessionId);
64+
65+
activeSessions.putIfAbsent(terminalSessionId, session);
66+
connectedSystem.setWebsocketSessionId(session.getId());
67+
68+
taskExecutor.execute(() -> {
69+
log.info("Listening to RDP server for session: {}", terminalSessionId);
70+
while (!Thread.currentThread().isInterrupted() &&
71+
activeSessions.get(terminalSessionId) != null &&
72+
!connectedSystem.getSession().getClosed()) {
73+
try {
74+
// Logic for receiving data from RDP server
75+
var rdpData = sessionTrackingService.getOutput(connectedSystem, 1L, TimeUnit.SECONDS,
76+
output -> (!connectedSystem.getSession().getClosed() &&
77+
(activeSessions.get(terminalSessionId) != null &&
78+
activeSessions.get(terminalSessionId).isOpen())));
79+
80+
// Send data to the specific terminal session
81+
if (rdpData != null) {
82+
for (Session.TerminalMessage terminalMessage : rdpData) {
83+
if (terminalMessage.getTrigger() == null) {
84+
sendToTerminalSession(terminalSessionId, connectedSystem, terminalMessage);
85+
}
86+
}
87+
for (Session.TerminalMessage terminalMessage : rdpData) {
88+
if (terminalMessage.getTrigger() != null) {
89+
sendToTerminalSession(terminalSessionId, connectedSystem, terminalMessage);
90+
}
91+
}
92+
} else {
93+
log.trace("No RDP data to return");
94+
}
95+
96+
} catch (Exception e) {
97+
log.error("Error while listening to RDP server: ", e);
98+
Thread.currentThread().interrupt(); // Ensure the thread can exit cleanly on exception
99+
}
100+
}
101+
log.trace("***Leaving RDP thread");
102+
});
103+
}
104+
105+
public void stopListeningToRdpServer(ConnectedSystem connectedSystem) {
106+
sessionTrackingService.closeSession(connectedSystem);
107+
}
108+
109+
private Session.TerminalMessage getTrigger(Trigger trigger) {
110+
var terminalMessage = Session.TerminalMessage.newBuilder();
111+
if (trigger.getAsk() != null) {
112+
terminalMessage.setType(Session.MessageType.PROMPT_DATA);
113+
} else {
114+
terminalMessage.setType(Session.MessageType.USER_DATA);
115+
}
116+
Session.Trigger.Builder triggerBuilder = Session.Trigger.newBuilder();
117+
switch(trigger.getAction()){
118+
case DENY_ACTION:
119+
triggerBuilder.setAction(Session.TriggerAction.DENY_ACTION);
120+
break;
121+
case JIT_ACTION:
122+
triggerBuilder.setAction(Session.TriggerAction.JIT_ACTION);
123+
break;
124+
case RECORD_ACTION:
125+
triggerBuilder.setAction(Session.TriggerAction.RECORD_ACTION);
126+
break;
127+
case APPROVE_ACTION:
128+
triggerBuilder.setAction(Session.TriggerAction.APPROVE_ACTION);
129+
break;
130+
case WARN_ACTION:
131+
triggerBuilder.setAction(Session.TriggerAction.WARN_ACTION);
132+
break;
133+
case PERSISTENT_MESSAGE:
134+
triggerBuilder.setAction(Session.TriggerAction.PERSISTENT_MESSAGE);
135+
break;
136+
case PROMPT_ACTION:
137+
triggerBuilder.setAction(Session.TriggerAction.PROMPT_ACTION);
138+
break;
139+
default:
140+
break;
141+
}
142+
triggerBuilder.setDescription(trigger.getDescription().isEmpty() ? "" : trigger.getDescription());
143+
terminalMessage.setTrigger(triggerBuilder.build());
144+
return terminalMessage.build();
145+
}
146+
147+
@Async
148+
public void sendToTerminalSession(String terminalSessionId, ConnectedSystem connectedSystem,
149+
Session.TerminalMessage rdpData) {
150+
DataSession session = activeSessions.get(terminalSessionId);
151+
log.info("Sending RDP message to session: {}", terminalSessionId);
152+
if (session != null && session.isOpen()) {
153+
try {
154+
byte[] messageBytes = rdpData.toByteArray();
155+
String base64Message = Base64.getEncoder().encodeToString(messageBytes);
156+
log.trace("Sending RDP message to session: {}", rdpData);
157+
session.sendMessage(new TextMessage(base64Message));
158+
} catch (IOException e) {
159+
log.error("Error sending RDP data to terminal session: " + terminalSessionId, e);
160+
}
161+
} else {
162+
log.debug("RDP Session {} is not available or closed", terminalSessionId);
163+
}
164+
}
165+
166+
public void processTerminalMessage(
167+
String sessionId, ConnectedSystem connectedSystem, String message) {
168+
169+
try {
170+
log.debug("Processing RDP terminal message for session: {}", sessionId);
171+
172+
// Process RDP-specific commands and data
173+
if (message.startsWith("RDP_COMMAND:")) {
174+
handleRdpCommand(connectedSystem, message.substring(12));
175+
} else {
176+
// Regular RDP data
177+
handleRdpData(connectedSystem, message);
178+
}
179+
180+
} catch (Exception e) {
181+
log.error("Error processing RDP terminal message", e);
182+
}
183+
}
184+
185+
private void handleRdpCommand(ConnectedSystem connectedSystem, String command) {
186+
log.info("Handling RDP command: {} for session: {}", command, connectedSystem.getSession().getId());
187+
// TODO: Implement RDP-specific command handling
188+
}
189+
190+
private void handleRdpData(ConnectedSystem connectedSystem, String data) {
191+
log.debug("Handling RDP data for session: {}", connectedSystem.getSession().getId());
192+
// TODO: Implement RDP data processing and forwarding
193+
}
194+
195+
public void removeSession(String sessionId) {
196+
log.trace("Removing RDP session: {}", sessionId);
197+
activeSessions.remove(sessionId);
198+
}
199+
}

docker/rdp-proxy/Dockerfile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Use an OpenJDK image as the base
2+
FROM eclipse-temurin:17-jdk-jammy
3+
4+
# Declare the argument
5+
ARG INCLUDE_DEV_CERTS=false
6+
7+
# Set environment so you can use in RUN
8+
ENV INCLUDE_DEV_CERTS=${INCLUDE_DEV_CERTS}
9+
10+
11+
# Set working directory
12+
WORKDIR /app
13+
14+
# Copy the pre-built API JAR into the container
15+
COPY rdpproxy.jar /app/rdpproxy.jar
16+
17+
18+
COPY dev-certs/sentrius-ca.crt /tmp/sentrius-ca.crt
19+
20+
RUN if [ "$INCLUDE_DEV_CERTS" = "true" ] && [ -f /tmp/sentrius-ca.crt ]; then \
21+
echo "Importing dev CA cert..." && \
22+
keytool -import -noprompt -trustcacerts \
23+
-alias sentrius-local-ca \
24+
-file /tmp/sentrius-ca.crt \
25+
-keystore "$JAVA_HOME/lib/security/cacerts" \
26+
-storepass changeit ; \
27+
else \
28+
echo "Skipping cert import"; \
29+
fi
30+
31+
32+
# Expose the port the app runs on
33+
EXPOSE 8080
34+
35+
RUN apt-get update && apt-get install -y curl
36+
37+
38+
# Command to run the app
39+
CMD ["java","-XX:+UseContainerSupport", "-jar", "/app/rdpproxy.jar", "--spring.config.location=/config/rdpproxy-application.properties"]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDJTCCAg2gAwIBAgIUDvcfbY2leSeMSnrsrJo2zv0ue/kwDQYJKoZIhvcNAQEL
3+
BQAwGjEYMBYGA1UEAwwPc2VudHJpdXMtZGV2LWNhMB4XDTI1MDcwMjIxNDk0MloX
4+
DTI2MDcwMjIxNDk0MlowGjEYMBYGA1UEAwwPc2VudHJpdXMtZGV2LWNhMIIBIjAN
5+
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0DDoRTDzG6QhQNy9tthyVnFIfBvS
6+
issnqzmpT3XrDdpHT0BIgYIBXWZzQbnhfnM1abCzZtn1ozmzUp84/PJbFYcupjNZ
7+
YUwul0C7BTAm8oN1vhQFbZ6u5iixHUsIbvxNb9IW8Yu003dtP1iXiaMcNZPr9xz7
8+
INgYigJuoSxtIEuzSBOFNYaXuUfn4r4GIlzF9lDnxeltvQqHTS5j4cdzXdis2e6k
9+
Gy+9OYZZp62WRHWTuhRfOakL1b+voTU8udyIS++mmxXy+AjHlzPuRB8L7wi3HoAM
10+
hBUxCzzJB3+mYNzyOd75bccbiWbMu1ay7WhOxxN2hxWJg+8u05bgAi4EPQIDAQAB
11+
o2MwYTAdBgNVHQ4EFgQU63Fomh1GrbWOavtqFoOhcboMAxMwHwYDVR0jBBgwFoAU
12+
63Fomh1GrbWOavtqFoOhcboMAxMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
13+
BAMCAQYwDQYJKoZIhvcNAQELBQADggEBAIu5heYvdV0r33avCMg82txjWvv7mXA5
14+
8BwU2GUsHqbh/0bS3Sxwc2KRsEh77NcgGo5Lr0gEftTzexGBjCikzhTL1+cWf6Ay
15+
b04NTr7E/EigZlZs/Ceoav5Mw7zElwDhtAr35OoQKTKBUHJgPKUAr5i2Ijwj8HYw
16+
ua/zUKU3RxRiuMTfsZmnzTJEtrTkgMbQN4HNRXTSmVPYNpYhVS+cPM9Xvy5QVaIR
17+
F2RxiywKSSzRY88w2c3sGXjDYs9wmxIWKbjNX51q2ZxwpF9E4c2s48eTjiVS5kVA
18+
/frlToZdVeLORjTtVw24RN4DTqsbOB3SkybylkopF8YjlkvEQNNZZ3c=
19+
-----END CERTIFICATE-----

ops-scripts/base/build-images.sh

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ update_integrationproxy=false
150150
update_launcher=false
151151
update_agent_proxy=false
152152
update_ssh_proxy=false
153+
update_rdp_proxy=false
154+
153155

154156
while [[ "$#" -gt 0 ]]; do
155157
case $1 in
@@ -162,7 +164,8 @@ while [[ "$#" -gt 0 ]]; do
162164
--sentrius-integration-proxy) update_integrationproxy=true ;;
163165
--sentrius-agent-proxy) update_agent_proxy=true ;;
164166
--sentrius-ssh-proxy) update_ssh_proxy=true ;;
165-
--all) update_sentrius=true; update_sentrius_ssh=true; update_sentrius_keycloak=true; update_sentrius_agent=true; update_sentrius_ai_agent=true; update_integrationproxy=true; update_launcher=true; update_agent_proxy=true; update_ssh_proxy=true; ;;
167+
--sentrius-rdp-proxy) update_rdp_proxy=true ;;
168+
--all) update_sentrius=true; update_sentrius_ssh=true; update_sentrius_keycloak=true; update_sentrius_agent=true; update_sentrius_ai_agent=true; update_integrationproxy=true; update_launcher=true; update_agent_proxy=true; update_ssh_proxy=true; update_rdp_proxy=true; ;;
166169
--no-cache) NO_CACHE=true ;;
167170
--include-dev-certs) INCLUDE_DEV_CERTS=true ;;
168171
*) echo "Unknown flag: $1"; exit 1 ;;
@@ -247,4 +250,12 @@ if $update_ssh_proxy; then
247250
build_image "sentrius-ssh-proxy" "$SSHPROXY_VERSION" "${SCRIPT_DIR}/../../docker/ssh-proxy"
248251
rm docker/ssh-proxy/sshproxy.jar
249252
update_env_var "SSHPROXY_VERSION" "$SSHPROXY_VERSION"
253+
fi
254+
255+
if $update_rdp_proxy; then
256+
cp rdp-proxy/target/rdp-proxy-*.jar docker/rdp-proxy/rdpproxy.jar
257+
RDPPROXY_VERSION=$(increment_patch_version $RDPPROXY_VERSION)
258+
build_image "sentrius-rdp-proxy" "$RDPPROXY_VERSION" "${SCRIPT_DIR}/../../docker/rdp-proxy"
259+
rm docker/rdp-proxy/rdpproxy.jar
260+
update_env_var "RDPPROXY_VERSION" "$RDPPROXY_VERSION"
250261
fi

ops-scripts/local/deploy-helm.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ helm upgrade --install sentrius ./sentrius-chart --namespace ${TENANT} \
261261
--set launcherservice.image.pullPolicy="Never" \
262262
--set launcherservice.image.tag=${LAUNCHER_VERSION} \
263263
--set sshproxy.image.tag=${SSHPROXY_VERSION} \
264+
--set rdpproxy.image.tag=${RDPPROXY_VERSION} \
264265
--set neo4j.env.NEO4J_server_config_strict__validation__enabled="\"false\"" \
265266
--set sentriusagent.image.tag=${SENTRIUS_AGENT_VERSION} || { echo "Failed to deploy Sentrius with Helm"; exit 1; }
266267

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<module>ai-agent</module>
2121
<module>agent-launcher</module>
2222
<module>ssh-proxy</module>
23+
<module>rdp-proxy</module>
2324
</modules>
2425
<properties>
2526
<java.version>17</java.version>

0 commit comments

Comments
 (0)