Skip to content

Commit 6c93aca

Browse files
committed
Minor updates
1 parent dda969d commit 6c93aca

File tree

26 files changed

+797
-50
lines changed

26 files changed

+797
-50
lines changed

.env

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
SENTRIUS_VERSION=1.1.38
2-
SENTRIUS_SSH_VERSION=1.1.4
3-
SENTRIUS_KEYCLOAK_VERSION=1.1.6
4-
SENTRIUS_AGENT_VERSION=1.1.4
5-
SENTRIUS_AI_AGENT_VERSION=1.1.18
6-
LLMPROXY_VERSION=1.0.4
1+
SENTRIUS_VERSION=1.1.61
2+
SENTRIUS_SSH_VERSION=1.1.16
3+
SENTRIUS_KEYCLOAK_VERSION=1.1.18
4+
SENTRIUS_AGENT_VERSION=1.1.16
5+
SENTRIUS_AI_AGENT_VERSION=1.1.30
6+
LLMPROXY_VERSION=1.0.16

README.md

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@ Sentrius
33
![image](docs/images/mainscreen.png)
44

55
Sentrius is zero trust (and if you want AI assisted) management system. to protect your infrastructure. It is split
6-
into
7-
two primary Maven. Currently we only support SSH, but RDP is in the works.
6+
into several maven projects. Currently we only support SSH, but RDP is in the works. Agents can be leveraged to monitor and control SSH sessions, ensuring that all connections are secure and compliant with your organization's policies.
87
sub-projects:
98

109
core – Handles the core functionalities (e.g., SSH session management, RDP, zero trust policy enforcement).
1110
api – Provides a RESTful API layer to interface with the core module.
12-
java-agent -- java based agent to monitor and control the ssh sessions.
13-
python-agent -- python based agent to monitor and control the ssh sessions.
11+
dataplane -- offers dataplane functionality for secure data transfer and processing.
12+
llm-proxy -- A proxy service that integrates with large language models (LLMs) to enhance security and compliance in SSH sessions.
13+
llm-dataplane -- A data processing layer that leverages LLMs for advanced analysis and decision-making in SSH
14+
sessions.
15+
ops-scripts -- Contains operational scripts for deployment and management tasks.
16+
ai-agent -- java based agent to monitor and control the ssh sessions.
17+
python-agent -- python based agent to monitor and control the ssh sessions and act on behalf of user (TBD).
1418

1519
Internally, Sentrius may still be referenced by its former name, SSO (SecureShellOps), in certain scripts or configurations.
1620
Table of Contents
@@ -74,7 +78,7 @@ sentrius/
7478

7579
Prerequisites
7680

77-
Java 11 or later
81+
Java 17 or later
7882
Apache Maven 3.6+
7983
Database (PostgreSQL, MySQL, etc.) for storing session and configuration data
8084
Keycloak for user authentication and authorization
@@ -90,10 +94,32 @@ cd sentrius
9094

9195
Running Sentrius
9296

93-
For convenience the ops/local directory contains a "run-sentrius.sh" script which will start the core and api modules. You can run this script from the project root:
97+
Build the projects from root ( mvn clean install ) to ensure all dependencies are resolved and the modules are compiled.
98+
99+
For convenience the ops/local directory contains a "run-sentrius.sh" script which will start the core and api
100+
modules. You can run this script from the project root.
101+
This assumes you have a database available, keycloak running, and the necessary configurations. We now require an
102+
OTEL endpoint, along with neo4j and kafka (but these are optional).:
94103

95104
./ops/local/run-sentrius.sh
96105

106+
It is simpler to run a kubernetes deployment, which is described in the Deployment. To do this, build as you would
107+
above.
108+
109+
Build the images in your local Docker registry (note this builds all images, including core, api, and any other modules):
110+
111+
/build-images-local.sh --all --no-cache
112+
113+
Run the Helm deployment script to deploy Sentrius to your local Kubernetes cluster:
114+
115+
./ops-scripts/local/deploy-helm.sh
116+
117+
There is a GCP deployment that is hasn't been tested in some time. You can find it in the ops-scripts/gcp directory.
118+
119+
You will need to ensure you link to your GKE cluster and have the necessary permissions to deploy resources.
120+
121+
./ops-scripts/gcp/deploy-helm.sh <helm-release-name> <gcp-project-id> <any-other-key-or-params>
122+
97123
You are welcome to run the core and api modules separately, as needed. You can start the core module by running:
98124

99125
mvn install
@@ -190,6 +216,6 @@ Contact
190216

191217
Questions, feedback, or need commercial support? Reach out to the project maintainers:
192218

193-
Email: support@sentrius.io
219+
Email: marc@sentrius.io
194220

195221
We’re always happy to help you secure your infrastructure with Sentrius!

api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java

Lines changed: 186 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.sentrius.sso.automation.sideeffects.SideEffect;
2424
import io.sentrius.sso.automation.sideeffects.SideEffectType;
2525
import io.sentrius.sso.core.config.SystemOptions;
26+
import io.sentrius.sso.core.model.ATPLPolicyEntity;
2627
import io.sentrius.sso.core.model.ConfigurationOption;
2728
import io.sentrius.sso.core.model.HostSystem;
2829
import io.sentrius.sso.core.dto.HostGroupDTO;
@@ -202,27 +203,30 @@ public List<SideEffect> initialize(InstallConfiguration installConfiguration, bo
202203

203204
var profiles = createHostGroups(sideEffects, rules, installConfiguration, action);
204205

205-
createUsers(sideEffects, installConfiguration, userTypes, profiles, action);
206+
// get the policies
207+
var policyList = createPolicies(installConfiguration, action);
208+
209+
createUsers(sideEffects, installConfiguration, userTypes, profiles, action, policyList);
206210

207211

208212

209213
// create automation assignments
210214

211215
//AppConfig.encryptProperty("initialized", Instant.now().toString());
212216

213-
// get the policies
214-
createPolicies(installConfiguration, action);
217+
215218

216219
return sideEffects;
217220
}
218221

219-
private void createPolicies(InstallConfiguration installConfiguration, boolean action) {
222+
private List<ATPLPolicyEntity> createPolicies(InstallConfiguration installConfiguration, boolean action) {
220223

224+
List<ATPLPolicyEntity> policyList = new ArrayList<>();
221225
installConfiguration.getAtplDefinitions().forEach( policy -> {
222-
atplPolicyService.savePolicy(policy);
226+
policyList.add( atplPolicyService.savePolicy(policy) );
223227

224228
});
225-
229+
return policyList;
226230
}
227231

228232
@Transactional
@@ -241,6 +245,137 @@ protected Map<String,ProfileRule> createRules(
241245
return rules;
242246
}
243247

248+
249+
@Transactional
250+
protected List<HostGroup> createHostGroups(
251+
List<SideEffect> sideEffects,
252+
Map<String, ProfileRule> rules,
253+
InstallConfiguration installConfiguration,
254+
boolean action
255+
) throws JSchException, GeneralSecurityException, IOException {
256+
257+
List<HostGroup> profiles = new ArrayList<>();
258+
259+
if (installConfiguration.getManagementGroups() == null) {
260+
return profiles;
261+
}
262+
263+
for (HostGroupConfigurationDTO hostGroupDto : installConfiguration.getManagementGroups()) {
264+
265+
HostGroup hostGroup = HostGroup.builder()
266+
.name(hostGroupDto.getDisplayName())
267+
.description(hostGroupDto.getDescription())
268+
.hostSystems(new ArrayList<>())
269+
.rules(new HashSet<>())
270+
.build();
271+
272+
if (action) {
273+
hostGroup.setApplicationKey(cryptoService.generateKeyPair(UUID.randomUUID().toString()));
274+
hostGroup = hostGroupRepository.save(hostGroup); // 🔑 Persist early
275+
}
276+
277+
List<HostSystem> associatedSystems = new ArrayList<>();
278+
279+
if (hostGroupDto.getSystems() != null) {
280+
for (String systemName : hostGroupDto.getSystems()) {
281+
for (var hostSystemDto : installConfiguration.getSystems()) {
282+
if (hostSystemDto.getDisplayName().equals(systemName)) {
283+
284+
List<HostSystem> existingSystems = systemRepository.findByDisplayNameAndHost(
285+
hostSystemDto.getDisplayName(),
286+
hostSystemDto.getHost()
287+
);
288+
289+
if (!existingSystems.isEmpty()) {
290+
for (HostSystem existing : existingSystems) {
291+
if (existing.getHostGroups() == null) {
292+
existing.setHostGroups(new ArrayList<>());
293+
}
294+
295+
HostGroup finalHostGroup = hostGroup;
296+
boolean alreadyLinked = existing.getHostGroups().stream()
297+
.anyMatch(g -> g.getName().equals(finalHostGroup.getName()));
298+
299+
if (!alreadyLinked) {
300+
existing.getHostGroups().add(hostGroup);
301+
associatedSystems.add(existing); // Add only if new relationship
302+
}
303+
}
304+
} else {
305+
HostSystem newSystem = HostSystem.fromDTO(hostSystemDto);
306+
newSystem.setHostGroups(new ArrayList<>(List.of(hostGroup)));
307+
associatedSystems.add(newSystem);
308+
}
309+
310+
break; // stop after match
311+
}
312+
}
313+
}
314+
}
315+
316+
// Assign rules
317+
if (hostGroupDto.getAssignedRules() != null) {
318+
for (String ruleName : hostGroupDto.getAssignedRules()) {
319+
ProfileRule rule = rules.get(ruleName);
320+
if (rule != null) {
321+
hostGroup.getRules().add(rule);
322+
rule.getHostGroups().add(hostGroup);
323+
}
324+
}
325+
}
326+
327+
List<HostGroup> existingGroups = hostGroupService.getHostGroupsByName(hostGroup.getName());
328+
329+
if (existingGroups.isEmpty()) {
330+
if (action) {
331+
hostGroup = hostGroupRepository.save(hostGroup);
332+
profiles.add(hostGroup);
333+
334+
// Save all systems that were linked above
335+
systemRepository.saveAll(associatedSystems);
336+
log.info("Created Host Enclave {} with {}", hostGroup.getId(), hostGroupDto.getDisplayName());
337+
}
338+
339+
sideEffects.add(SideEffect.builder()
340+
.sideEffectDescription("Creating Host Enclave " + hostGroupDto.getDisplayName())
341+
.type(SideEffectType.UPDATE_DATABASE)
342+
.asset("HostGroups")
343+
.build());
344+
345+
} else {
346+
for (HostGroup existingGroup : existingGroups) {
347+
profiles.add(existingGroup);
348+
for (HostSystem hs : associatedSystems) {
349+
if (hs.getHostGroups() == null) {
350+
hs.setHostGroups(new ArrayList<>());
351+
}
352+
353+
boolean alreadyAssigned = hs.getHostGroups().stream()
354+
.anyMatch(g -> g.getId().equals(existingGroup.getId()));
355+
356+
if (!alreadyAssigned) {
357+
hs.getHostGroups().add(existingGroup);
358+
if (action) {
359+
systemRepository.save(hs);
360+
log.info("Updated Host Enclave {} with {}", existingGroup.getId(), hs.getId());
361+
}
362+
sideEffects.add(SideEffect.builder()
363+
.sideEffectDescription("Updating Host Enclave " + hostGroupDto.getDisplayName())
364+
.type(SideEffectType.UPDATE_DATABASE)
365+
.asset("HostGroups")
366+
.build());
367+
}
368+
}
369+
}
370+
}
371+
}
372+
373+
return profiles;
374+
}
375+
376+
377+
378+
/*
244379
@Transactional
245380
protected List<HostGroup> createHostGroups(List<SideEffect> sideEffects, Map<String,ProfileRule> rules, InstallConfiguration installConfiguration,
246381
boolean action)
@@ -313,6 +448,23 @@ protected List<HostGroup> createHostGroups(List<SideEffect> sideEffects, Map<Str
313448
log.info("Updating Host Enclave {} with {}", hg.getId(), hg.getId());
314449
profiles.add(hg);
315450
451+
for (var hs : hostGroup.getHostSystems()) {
452+
boolean alreadyAssigned = hs.getHostGroups().stream().anyMatch(g -> g.getId().equals(hg.getId()));
453+
if (!alreadyAssigned) {
454+
if (action) {
455+
hs.getHostGroups().add(hg);
456+
systemRepository.save(hs);
457+
log.info("Updating Host Enclave {} with {}", hg.getId(), hs.getId());
458+
}
459+
460+
sideEffects.add(SideEffect.builder()
461+
.sideEffectDescription("Updating Host Enclave " + hostGroupDto.getDisplayName())
462+
.type(SideEffectType.UPDATE_DATABASE)
463+
.asset("HostGroups")
464+
.build());
465+
}
466+
}
467+
/*
316468
for(var hs : hostGroup.getHostSystems()) {
317469
if (!systemRepository.isAssignedToHostGroups(hs.getId(), List.of( hg.getId()))) {
318470
if (action) {
@@ -334,7 +486,7 @@ protected List<HostGroup> createHostGroups(List<SideEffect> sideEffects, Map<Str
334486
}
335487
return profiles;
336488
}
337-
489+
*/
338490
@Transactional
339491
protected List<SideEffect> createSystems(InstallConfiguration installConfiguration, boolean action) throws SQLException,
340492
GeneralSecurityException {
@@ -423,13 +575,17 @@ protected List<UserType> createUserTypes(List<SideEffect> sideEffects, InstallCo
423575
@Transactional
424576
protected List<User> createUsers(
425577
List<SideEffect> sideEffects, InstallConfiguration installConfiguration, List<UserType> userTypes,
426-
List<HostGroup> profiles, boolean action)
578+
List<HostGroup> profiles, boolean action, List<ATPLPolicyEntity> policyList
579+
)
427580
throws SQLException, GeneralSecurityException {
428581
List<User> users = new ArrayList<>();
429582
Map<Long, Set<Long>> assignments = new HashMap<>();
430583
if (null != installConfiguration.getUsers()) {
431584
for (var userDTO : installConfiguration.getUsers()) {
432-
585+
if (userDTO.getPassword() == null || userDTO.getPassword().isEmpty()) {
586+
log.warn("User {} has no password");
587+
userDTO.setPassword(UUID.randomUUID().toString());
588+
}
433589
// set the UserType from the configuration
434590
if (null != userDTO.getAuthorizationType()) {
435591
for (UserType type : userTypes) {
@@ -448,15 +604,18 @@ protected List<User> createUsers(
448604
userService.getUserType(UserType.builder().id(userDTO.getAuthorizationType().getId()).build());
449605

450606
User user = User.from(userDTO, type.get());
607+
608+
609+
451610
User finalUser = user;
452611
userService.findByUsername(user.getUsername()).ifPresentOrElse(
453612
user1 -> {
454613
finalUser.setId(user1.getId());
455614
},
456615
() -> {
457616
if (action) {
458-
var usr = userService.addUscer(user);
459-
user.setId(usr.getId());
617+
var usr = userService.addUscer(finalUser);
618+
finalUser.setId(usr.getId());
460619
}
461620
sideEffects.add(SideEffect.builder().sideEffectDescription("Creating user " + userDTO.getUsername()).type(
462621
SideEffectType.UPDATE_DATABASE).asset("Users").build());
@@ -513,6 +672,21 @@ protected List<User> createUsers(
513672
}
514673
}
515674
}
675+
if (action){
676+
user = userService.getUser(user.getId());
677+
var definition = userDTO.getAtlpDefinition();
678+
if (null != definition && !definition.isEmpty()) {
679+
Optional<ATPLPolicyEntity> policy = policyList.stream()
680+
.filter(p -> p.getPolicyId().equals(definition))
681+
.findFirst();
682+
if (policy.isPresent()) {
683+
atplPolicyService.assignPolicyToUser(user, policy.get());
684+
} else {
685+
log.warn("No ATPL policy found for user {} with policy id {}", user.getUsername(),
686+
definition);
687+
}
688+
}
689+
}
516690
}
517691
}
518692
for (var entry : assignments.entrySet()) {
@@ -577,7 +751,7 @@ protected void createSystemUser(InstallConfiguration connection) throws NoSuchAl
577751
.id(0L)
578752
.authorizationType(UserType.createSuperUser())
579753
.username("SYSTEM")
580-
.password(UUID.randomUUID().toString() + "." + UUID.randomUUID().toString())
754+
.password(UUID.randomUUID() + UUID.randomUUID().toString())
581755
.build();
582756

583757
if (null == user) {

api/src/main/resources/application.properties

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,19 @@ otel.exporter.otlp.timeout=10s
9191

9292
sentrius.agent.register.bootstrap.allow=true
9393
sentrius.agent.bootstrap.policy=default-policy.yaml
94+
# Optional: set the identity lifetime
95+
sentrius.agent.register.bootstrap.identity.recreate
9496

9597
provenance.kafka.topic=sentrius-provenance
9698
spring.kafka.bootstrap-servers=home.guard.local:9092
9799
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
98100
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
99101

100102
# Optional: trust package to avoid class cast issues with JSON
101-
spring.kafka.producer.properties.spring.json.trusted.packages=io.sentrius.*
103+
spring.kafka.producer.properties.spring.json.trusted.packages=io.sentrius.*
104+
105+
spring.kafka.producer.retries=0
106+
spring.kafka.producer.acks=1
107+
spring.kafka.producer.request-timeout-ms=500
108+
spring.kafka.producer.delivery-timeout-ms=1000
109+
spring.kafka.properties.max.block.ms=500

0 commit comments

Comments
 (0)