diff --git a/.gcp.env b/.gcp.env index 88461cae..70175931 100644 --- a/.gcp.env +++ b/.gcp.env @@ -1,4 +1,4 @@ -SENTRIUS_VERSION=1.0.18 +SENTRIUS_VERSION=1.0.23 SENTRIUS_SSH_VERSION=1.0.2 SENTRIUS_KEYCLOAK_VERSION=1.0.4 -SENTRIUS_AGENT_VERSION=1.0.13 \ No newline at end of file +SENTRIUS_AGENT_VERSION=1.0.14 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f3056b56..cc27c73c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ WORKDIR /app # Copy the pre-built API JAR into the container COPY api/target/sentrius-api-1.0.0-SNAPSHOT.jar /app/sentrius.jar COPY docker/sentrius/exampleInstallWithTypes.yml /app/exampleInstallWithTypes.yml +COPY docker/sentrius/demoInstaller.yml /app/demoInstaller.yml # Expose the port the app runs on EXPOSE 8080 diff --git a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java index a103bfd1..eb8f0a89 100644 --- a/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java +++ b/analyagents/src/main/java/io/sentrius/agent/analysis/agents/sessions/SessionAnalyticsAgent.java @@ -4,28 +4,37 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; import io.sentrius.sso.core.model.metadata.AnalyticsTracking; import io.sentrius.sso.core.model.metadata.TerminalBehaviorMetrics; import io.sentrius.sso.core.model.metadata.TerminalCommand; import io.sentrius.sso.core.model.metadata.TerminalRiskIndicator; import io.sentrius.sso.core.model.metadata.TerminalSessionMetadata; import io.sentrius.sso.core.model.metadata.UserExperienceMetrics; +import io.sentrius.sso.core.model.security.IntegrationSecurityToken; import io.sentrius.sso.core.model.sessions.TerminalLogs; import io.sentrius.sso.core.repository.AnalyticsTrackingRepository; +import io.sentrius.sso.core.services.IntegrationSecurityTokenService; +import io.sentrius.sso.core.services.PluggableServices; import io.sentrius.sso.core.services.SessionService; import io.sentrius.sso.core.services.metadata.TerminalBehaviorMetricsService; import io.sentrius.sso.core.services.metadata.TerminalCommandService; import io.sentrius.sso.core.services.metadata.TerminalRiskIndicatorService; import io.sentrius.sso.core.services.metadata.TerminalSessionMetadataService; import io.sentrius.sso.core.services.metadata.UserExperienceMetricsService; +import io.sentrius.sso.core.utils.JsonUtil; +import io.sentrius.sso.integrations.external.ExternalIntegrationDTO; +import io.sentrius.sso.security.ApiKey; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -42,6 +51,8 @@ public class SessionAnalyticsAgent { private final UserExperienceMetricsService experienceMetricsService; private final AnalyticsTrackingRepository trackingRepository; private final SessionService sessionService; + final IntegrationSecurityTokenService integrationSecurityTokenService; + @Scheduled(fixedDelay = 60000) // Waits 60 seconds after the previous run completes @Transactional @@ -66,7 +77,35 @@ public void processSessions() { // session.setSessionStatus("PROCESSED"); // sessionMetadataService.saveSession(session); } + + log.info("Finished processing sessions"); } +/* TODO - Implement this + @Scheduled(fixedDelay = 60000) // Waits 60 seconds after the previous run completes + @Transactional + public void processTerminalCommands() { + log.info("Processing terminal commands..."); + + var openaiService = integrationSecurityTokenService.findByConnectionType("openai").stream().findFirst().orElse(null); + + if (null != openaiService){ + log.info("OpenAI service is available"); + ExternalIntegrationDTO externalIntegrationDTO = null; + try { + externalIntegrationDTO = JsonUtil.MAPPER.readValue(openAiToken.getConnectionInfo(), + ExternalIntegrationDTO.class); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + ApiKey key = + ApiKey.builder().apiKey(externalIntegrationDTO.getApiToken()).principal(externalIntegrationDTO.getUsername()).build(); + + } else { + log.info("OpenAI service is not enabled"); + } + + log.info("Finished processing terminal commands"); + }*/ private void processSession(TerminalSessionMetadata session) { diff --git a/api/dynamic.properties b/api/dynamic.properties index 9ac5989c..85368675 100644 --- a/api/dynamic.properties +++ b/api/dynamic.properties @@ -22,4 +22,4 @@ enableInternalAudit=true twopartyapproval.require.explanation.LOCKING_SYSTEMS=false canApproveOwnJITs=false allowUploadSystemConfiguration = true -yamlConfigurationPath=/mnt/ExtraDrive/repos/Sentrius/docker/sentrius/exampleInstallWithTypes.yml \ No newline at end of file +yamlConfigurationPath=/mnt/ExtraDrive/repos/Sentrius/docker/sentrius/demoInstaller.yml \ No newline at end of file diff --git a/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java b/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java index b715e84c..86b0940e 100644 --- a/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java +++ b/api/src/main/java/io/sentrius/sso/startup/ConfigurationApplicationTask.java @@ -19,6 +19,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import com.jcraft.jsch.JSchException; +import io.sentrius.sso.automation.auditing.RuleFactory; import io.sentrius.sso.automation.sideeffects.SideEffect; import io.sentrius.sso.automation.sideeffects.SideEffectType; import io.sentrius.sso.core.config.SystemOptions; @@ -28,6 +29,7 @@ import io.sentrius.sso.core.model.dto.HostSystemDTO; import io.sentrius.sso.core.model.dto.UserTypeDTO; import io.sentrius.sso.core.model.hostgroup.HostGroup; +import io.sentrius.sso.core.model.hostgroup.ProfileRule; import io.sentrius.sso.core.model.security.UserType; import io.sentrius.sso.core.model.security.enums.ApplicationAccessEnum; import io.sentrius.sso.core.model.security.enums.AutomationAccessEnum; @@ -43,9 +45,12 @@ import io.sentrius.sso.core.repository.UserTypeRepository; import io.sentrius.sso.core.security.service.CryptoService; import io.sentrius.sso.core.services.HostGroupService; +import io.sentrius.sso.core.services.RuleService; import io.sentrius.sso.core.services.UserService; import io.sentrius.sso.install.configuration.InstallConfiguration; import io.sentrius.sso.install.configuration.dtos.HostGroupConfigurationDTO; +import io.sentrius.sso.install.configuration.dtos.RuleDTO; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationReadyEvent; @@ -70,12 +75,15 @@ public class ConfigurationApplicationTask { final UserTypeRepository userTypeRepository; + final RuleService ruleService; + final UserService userService; private final HostGroupService hostGroupService; final CryptoService cryptoService; @EventListener(ApplicationReadyEvent.class) + @Transactional public void afterStartup() throws IOException, GeneralSecurityException, JSchException, SQLException { // Your logic here @@ -131,7 +139,7 @@ public void afterStartup() throws IOException, GeneralSecurityException, JSchExc log.info("No configuration file found"); } } - + @Transactional public List createStaticType(UserType type, boolean action) throws SQLException, GeneralSecurityException { @@ -160,7 +168,7 @@ public List createStaticType(UserType type, boolean action) throws S return sideEffects; } - + @Transactional public List initialize(InstallConfiguration installConfiguration, boolean action) throws SQLException, GeneralSecurityException, @@ -188,19 +196,38 @@ public List initialize(InstallConfiguration installConfiguration, bo // create profiles and assign systems - var profiles = createHostGroups(sideEffects, installConfiguration, action); + var rules = createRules(sideEffects, installConfiguration, action); + + var profiles = createHostGroups(sideEffects, rules, installConfiguration, action); createUsers(sideEffects, installConfiguration, userTypes, profiles, action); - + + + // create automation assignments //AppConfig.encryptProperty("initialized", Instant.now().toString()); return sideEffects; } + @Transactional + protected Map createRules( + List sideEffects, InstallConfiguration installConfiguration, + boolean action + ) { + Map rules = new HashMap<>(); + var configuredRules = installConfiguration.getRules(); + for(RuleDTO rule : configuredRules) { + ProfileRule newRule = + ProfileRule.builder().ruleClass(rule.getRuleClass()).ruleName(rule.getDisplayName()).ruleConfig(rule.getConfiguration()).build(); + newRule = ruleService.saveRule(newRule); + rules.put(rule.getDisplayName(), newRule); + } + return rules; + } - - private List createHostGroups(List sideEffects, InstallConfiguration installConfiguration, + @Transactional + protected List createHostGroups(List sideEffects, Map rules, InstallConfiguration installConfiguration, boolean action) throws JSchException, GeneralSecurityException, IOException { List profiles = new ArrayList<>(); @@ -235,12 +262,21 @@ private List createHostGroups(List sideEffects, InstallCo if (!systems.isEmpty()) { hostGroup.setHostSystems(systems); } + + for( var assignedRule : hostGroupDto.getAssignedRules() ) { + if (rules.containsKey(assignedRule)) { + var rule = rules.get(assignedRule); + hostGroup.getRules().add(rule); + rule.getHostGroups().add(hostGroup); + } + }; } var hostGroups = hostGroupService.getHostGroupsByName(hostGroup.getName()); if (hostGroups.isEmpty()) { if (action) { hostGroup = hostGroupRepository.save(hostGroup); + log.info("Creating Host Group {} with {}", hostGroup.getId(), hostGroupDto.getDisplayName()); profiles.add(hostGroup); for(var hs : hostGroup.getHostSystems()) { if (null == hs.getHostGroups()){ @@ -284,7 +320,8 @@ private List createHostGroups(List sideEffects, InstallCo return profiles; } - private List createSystems(InstallConfiguration installConfiguration, boolean action) throws SQLException, + @Transactional + protected List createSystems(InstallConfiguration installConfiguration, boolean action) throws SQLException, GeneralSecurityException { List sideEffects = new ArrayList<>(); if (null != installConfiguration.getSystems()) { @@ -303,7 +340,8 @@ private List createSystems(InstallConfiguration installConfiguration return sideEffects; } - private boolean shouldInsertSystem(HostSystem systemObj) { + @Transactional + protected boolean shouldInsertSystem(HostSystem systemObj) { var systems = systemRepository.findByDisplayName(systemObj.getDisplayName()); if (systems.isEmpty()) { return true; @@ -316,6 +354,7 @@ private boolean shouldInsertSystem(HostSystem systemObj) { return true; } + @Transactional protected List createUserTypes(List sideEffects, InstallConfiguration installConfiguration, boolean action) throws SQLException, GeneralSecurityException { List types = new ArrayList<>(); @@ -365,6 +404,7 @@ protected List createUserTypes(List sideEffects, InstallCo return types; } + @Transactional protected List createUsers( List sideEffects, InstallConfiguration installConfiguration, List userTypes, List profiles, boolean action) @@ -466,6 +506,8 @@ static void createOrUpdate(Map> assignments, Long profileId, Lon set.add(userId); } + + @Transactional protected List createAdminUser(InstallConfiguration installConfiguration, boolean action) throws NoSuchAlgorithmException { var user = installConfiguration.getAdminUser(); @@ -501,6 +543,7 @@ protected List createAdminUser(InstallConfiguration installConfigura return sideEffects; } + @Transactional protected void createSystemUser(InstallConfiguration connection) throws NoSuchAlgorithmException { User user = User.builder() diff --git a/core/src/main/java/io/sentrius/sso/automation/auditing/RuleFactory.java b/core/src/main/java/io/sentrius/sso/automation/auditing/RuleFactory.java index 23b91b7b..8dd7daa1 100644 --- a/core/src/main/java/io/sentrius/sso/automation/auditing/RuleFactory.java +++ b/core/src/main/java/io/sentrius/sso/automation/auditing/RuleFactory.java @@ -7,6 +7,7 @@ import io.sentrius.sso.core.model.auditing.Rule; import io.sentrius.sso.core.services.PluggableServices; import io.sentrius.sso.core.services.terminal.SessionTrackingService; +import io.sentrius.sso.install.configuration.dtos.RuleDTO; import lombok.extern.slf4j.Slf4j; @Slf4j @@ -41,4 +42,5 @@ public static void createRules( } } } + } diff --git a/core/src/main/java/io/sentrius/sso/automation/auditing/rules/SudoPrevention.java b/core/src/main/java/io/sentrius/sso/automation/auditing/rules/SudoPrevention.java index 7192b1d7..281009d5 100644 --- a/core/src/main/java/io/sentrius/sso/automation/auditing/rules/SudoPrevention.java +++ b/core/src/main/java/io/sentrius/sso/automation/auditing/rules/SudoPrevention.java @@ -3,9 +3,17 @@ import java.util.Optional; import io.sentrius.sso.automation.auditing.Trigger; import io.sentrius.sso.automation.auditing.TriggerAction; +import lombok.extern.slf4j.Slf4j; +import java.util.Optional; +import java.util.regex.Pattern; + +@Slf4j public class SudoPrevention extends CommandEvaluator { + // Updated pattern to match both 'sudo' and 'su' as standalone commands (case-insensitive) + private static final Pattern SUDO_SU_PATTERN = Pattern.compile("\\b(sudo|su)\\b", Pattern.CASE_INSENSITIVE); + public SudoPrevention() { action = TriggerAction.DENY_ACTION; isSanitized = true; @@ -13,8 +21,15 @@ public SudoPrevention() { @Override public Optional trigger(String text) { - if (text.contains("sudo") || text.contains("SUDO")) { - return Optional.of(new Trigger(action, "SUDO is not allowed")); + if (text == null || text.isEmpty()) { + return Optional.empty(); + } + + // Check if the input contains 'sudo' or 'su' + if (SUDO_SU_PATTERN.matcher(text).find()) { + // Log the blocked attempt + log.info("Blocked SUDO/SU attempt: {}", text); + return Optional.of(new Trigger(action, "SUDO and SU are not allowed")); } return Optional.empty(); diff --git a/core/src/main/java/io/sentrius/sso/core/model/hostgroup/HostGroup.java b/core/src/main/java/io/sentrius/sso/core/model/hostgroup/HostGroup.java index 923e5acf..d3aa67e7 100755 --- a/core/src/main/java/io/sentrius/sso/core/model/hostgroup/HostGroup.java +++ b/core/src/main/java/io/sentrius/sso/core/model/hostgroup/HostGroup.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; import com.fasterxml.jackson.annotation.JsonManagedReference; @@ -89,9 +90,10 @@ public class HostGroup { joinColumns = @JoinColumn(name = "system_id"), inverseJoinColumns = @JoinColumn(name = "rule_id") ) - private Set rules; + @Builder.Default + private Set rules = new HashSet<>(); - @OneToOne(cascade = CascadeType.ALL) + @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REMOVE}) @JoinColumn(name = "application_key_id", referencedColumnName = "id", unique = true) private ApplicationKey applicationKey; diff --git a/core/src/main/java/io/sentrius/sso/core/model/hostgroup/ProfileRule.java b/core/src/main/java/io/sentrius/sso/core/model/hostgroup/ProfileRule.java index b5bf8efa..7ae197d2 100755 --- a/core/src/main/java/io/sentrius/sso/core/model/hostgroup/ProfileRule.java +++ b/core/src/main/java/io/sentrius/sso/core/model/hostgroup/ProfileRule.java @@ -1,5 +1,6 @@ package io.sentrius.sso.core.model.hostgroup; +import java.util.HashSet; import java.util.Set; import io.sentrius.sso.core.model.auditing.Rule; import jakarta.persistence.Column; @@ -11,6 +12,7 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.Table; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -41,6 +43,7 @@ public class ProfileRule extends Rule { private String ruleConfig; @ManyToMany(mappedBy = "rules", fetch = FetchType.EAGER) - private Set hostGroups; + @Builder.Default + private Set hostGroups = new HashSet<>(); } \ No newline at end of file diff --git a/core/src/main/java/io/sentrius/sso/core/security/service/CryptoService.java b/core/src/main/java/io/sentrius/sso/core/security/service/CryptoService.java index 0dfe5e9f..72eb33ba 100644 --- a/core/src/main/java/io/sentrius/sso/core/security/service/CryptoService.java +++ b/core/src/main/java/io/sentrius/sso/core/security/service/CryptoService.java @@ -15,6 +15,7 @@ import io.sentrius.sso.core.config.SystemOptions; import io.sentrius.sso.core.model.ApplicationKey; import io.sentrius.sso.core.repository.ApplicationKeyRepository; +import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -92,6 +93,7 @@ public String encodePassword(String password) throws NoSuchAlgorithmException { * * @return passphrase for system generated key */ + @Transactional public ApplicationKey generateKeyPair(String passphrase) throws JSchException, IOException, GeneralSecurityException { diff --git a/core/src/main/java/io/sentrius/sso/genai/suggestions/TerminalSuggestors.java b/core/src/main/java/io/sentrius/sso/genai/suggestions/TerminalSuggestors.java new file mode 100644 index 00000000..537c2ebb --- /dev/null +++ b/core/src/main/java/io/sentrius/sso/genai/suggestions/TerminalSuggestors.java @@ -0,0 +1,88 @@ +package io.sentrius.sso.genai.suggestions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.sentrius.sso.core.model.LLMResponse; +import io.sentrius.sso.core.utils.JsonUtil; +import io.sentrius.sso.genai.DataGenerator; +import io.sentrius.sso.genai.GenerativeAPI; +import io.sentrius.sso.genai.GeneratorConfiguration; +import io.sentrius.sso.genai.Response; +import io.sentrius.sso.genai.TerminalLogConfiguration; +import io.sentrius.sso.genai.model.TwoPartyRequest; +import io.sentrius.sso.genai.model.endpoints.ChatApiEndpointRequest; +import io.sentrius.sso.integrations.exceptions.HttpException; +import io.sentrius.sso.security.TokenProvider; +import lombok.extern.slf4j.Slf4j; +import org.thymeleaf.util.StringUtils; + +/** + * Query compliance scorer with user defined rules to be provided to OpenAI + */ +@Slf4j +public class TerminalSuggestors extends DataGenerator { + + public TerminalSuggestors( + TokenProvider token, GenerativeAPI generator, GeneratorConfiguration config, + TerminalLogConfiguration complianceConfig) { + super(token, generator, config); + } + + /** + * Generates input for the generative AI endpoint. + * + * @return Question to be asked to the generative AI endpoint. + */ + @Override + public String generateInput(TwoPartyRequest on) { + String queryStr = on.getSystemInput(); //" Here is the user's objective: " + on.getUserObjective() + "."; + + + return queryStr; + } + + public LLMResponse generate(TwoPartyRequest on) throws HttpException, JsonProcessingException { + ChatApiEndpointRequest request = + ChatApiEndpointRequest.builder().userInput(generateUserInput( on)).systemInput(generateInput(on)).build(); + log.info("Generating compliance score for: " + on); + request.setTemperature(0.8f); + Response hello = api.sample(request, Response.class); + try{ + var resp = hello.concatenateResponses(); + var objectNode = JsonUtil.MAPPER.readValue(resp, ObjectNode.class); + var dbl = Double.valueOf(objectNode.get("score").asText()); + log.info("score would be {}", resp); + log.info("question would be {}", objectNode.get("question").asText()); + var bldr = LLMResponse.builder(); + if (objectNode.has("question")){ + bldr.question(objectNode.get("question").asText()); + } + return bldr.score(dbl).response(objectNode.get("explanation").asText()).build(); + } catch (Exception e) { + log.info("Error parsing compliance score: " + hello.concatenateResponses()); + return LLMResponse.builder().score(0.0).response("Error parsing compliance score").build(); + } + } + + private String generateUserInput(TwoPartyRequest userInput) { + String queryStr = ""; + if (!StringUtils.isEmpty(userInput.getPreviousPrompt()) && !StringUtils.isEmpty(userInput.getPromptResponse())) { + queryStr += "You previously asked the user of their session: " + userInput.getPreviousPrompt() + ". This" + + " " + + "was their response: " + userInput.getPromptResponse() + ". Can you incorporate this information into " + + "your risk assessment and confidence score? If they are avoiding your question don't re-ask, just raise " + + "your risk score."; + } + queryStr += "Can you give me a confidence score from 0 to 1, to two decimal places, on whether the next 10 " + + "terminal log " + + "output" + + " from" + + " a single terminal session are exhibiting risky behavior, where 0 is normal and 1 would be a session you" + + " acting as a human would ask to stop. Provide the score, an explanation of why, and a question to ask " + + "the user, but only if needed, in JSON with the fields score, explanation, and question. Here is the " + + "user's last ten " + + "commands: " + userInput.getUserInput() + "."; + return queryStr; + } + +} \ No newline at end of file diff --git a/core/src/main/java/io/sentrius/sso/install/configuration/InstallConfiguration.java b/core/src/main/java/io/sentrius/sso/install/configuration/InstallConfiguration.java index 4865e6f5..01b158e9 100644 --- a/core/src/main/java/io/sentrius/sso/install/configuration/InstallConfiguration.java +++ b/core/src/main/java/io/sentrius/sso/install/configuration/InstallConfiguration.java @@ -13,6 +13,7 @@ import io.sentrius.sso.core.model.security.enums.CertKeyConfiguration; import io.sentrius.sso.core.model.security.enums.SystemKeyConfiguration; import io.sentrius.sso.install.configuration.dtos.HostGroupConfigurationDTO; +import io.sentrius.sso.install.configuration.dtos.RuleDTO; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -30,6 +31,9 @@ public class InstallConfiguration { private List users; + @Builder.Default + private List rules = new ArrayList<>(); + @Builder.Default private UserDTO adminUser = UserDTO.builder() diff --git a/core/src/main/java/io/sentrius/sso/install/configuration/dtos/HostGroupConfigurationDTO.java b/core/src/main/java/io/sentrius/sso/install/configuration/dtos/HostGroupConfigurationDTO.java index 8e34d395..586ef740 100644 --- a/core/src/main/java/io/sentrius/sso/install/configuration/dtos/HostGroupConfigurationDTO.java +++ b/core/src/main/java/io/sentrius/sso/install/configuration/dtos/HostGroupConfigurationDTO.java @@ -1,8 +1,10 @@ package io.sentrius.sso.install.configuration.dtos; +import java.util.ArrayList; import java.util.List; import io.sentrius.sso.core.model.hostgroup.ProfileConfiguration; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; @@ -10,6 +12,7 @@ @Getter @Setter +@Builder @Data @NoArgsConstructor @AllArgsConstructor @@ -17,5 +20,7 @@ public class HostGroupConfigurationDTO { private String displayName; private String description; private List systems; + @Builder.Default + private List assignedRules = new ArrayList<>(); private ProfileConfiguration configuration; } diff --git a/core/src/main/java/io/sentrius/sso/install/configuration/dtos/RuleDTO.java b/core/src/main/java/io/sentrius/sso/install/configuration/dtos/RuleDTO.java new file mode 100644 index 00000000..99cebd24 --- /dev/null +++ b/core/src/main/java/io/sentrius/sso/install/configuration/dtos/RuleDTO.java @@ -0,0 +1,21 @@ +package io.sentrius.sso.install.configuration.dtos; + +import java.util.List; +import io.sentrius.sso.core.model.hostgroup.ProfileConfiguration; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RuleDTO { + private String displayName; + private String description; + private String ruleClass; + private String configuration; +} \ No newline at end of file diff --git a/docker/sentrius/demoInstaller.yml b/docker/sentrius/demoInstaller.yml new file mode 100644 index 00000000..4cf3fc51 --- /dev/null +++ b/docker/sentrius/demoInstaller.yml @@ -0,0 +1,118 @@ +userTypes: + - userTypeName: testType + systemAccess: CAN_VIEW_SYSTEMS + ruleAccess: CAN_DEL_RULES + - userTypeName: adminType + systemAccess: CAN_MANAGE_SYSTEMS + ruleAccess: CAN_MANAGE_RULES + applicationAccess: CAN_MANAGE_APPLICATION + ztAccessTokenAccess: CAN_MANAGE_ZTATS + - userTypeName: jitType + systemAccess: CAN_VIEW_SYSTEMS + ztAccessTokenAccess: CAN_MANAGE_ZTATS +users: + - username: test + name: firstname lastname + password: test + authorizationType: + userTypeName: testType + hostGroups: + - displayName: testGroup + - username: jit + name: firstname lastname + password: jit + authorizationType: + userTypeName: jitType + hostGroups: + - displayName: testGroup + +rules: + - displayName: deleteRule + ruleClass: io.sentrius.sso.automation.auditing.rules.DeletePrevention + description: Don't allow deletion of files + configuration: EOL + +systems: + - displayName: SSH-HOST + sshUser: ubuntu + port: 22 + host: sentrius-ssh + authorizedKeys: ~/.ssh/authorized_keys + - displayName: UNFETTERED-SSH-HOST + sshUser: ubuntu + port: 22 + host: sentrius-bad-ssh + authorizedKeys: ~/.ssh/authorized_keys + +## Define groups of users who are assigned to systems +## also entails the configuration that is applied to groupf +## Some users may not have access to all systems in the group +## or may have restricted accesses to systems. +managementGroups: + - displayName: testGroup + description: test group + systems: + - SSH-HOST + configuration: + configurationName: testConfig + terminalsLocked: false + allowSudo: false + assignedRules: + - deleteRule + - displayName: unfetteredGroup + description: Unfettered test group + systems: + - UNFETTERED-SSH-HOST + configuration: + configurationName: testConfig + terminalsLocked: false + allowSudo: true +systemKeyConfigurations: + - keyConfigurationName: testConfig + #can also include paths. Note that this private key should not be used + ## for production purposes. + #pathToPrivateKey: /home/user/.ssh/id_rsa + #pathToPublicKey: /home/user/.ssh/id_rsa.pub + privateKey: | + -----BEGIN OPENSSH PRIVATE KEY----- + b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCfQpOIo/ + +tvZqi8Yg9rbBEAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDmanENTLBj + xp7ZwbNoNaIU9nl7dIPAm0yyVAKPs3d3GSw6VEAxQIEAbPxygxlQta5YZ6oLKVIA7oUadg + /r7fWo095Ah9IPDvZOgV1Z0LVM/qGSBLFFMIZqyeA+N28M9LfO2mG2vLrvWRv1jbCKUVOg + nWiTisU78ubV26zScTelJh/UQE4bAkdRfs+YfJMvkNm1LpoHIKaaKtSgTrf912L/cIPbW3 + sM5Vi6j7mR0/Ya2+q+uacpTPL4EMRmF8Fg6/F/OcqIjUcsr5FtI6owXu2GWuIeud28DqHV + xXqEZ6ZYR2/J5Y/fOEoTpSJ2fNwvCl1fm2NM8a+Ndngokb40zsn8lDwfslEefRcZfPrDEe + 97s+kmP5ed5s/xpyVAy3YgAF21HUWqTu4GrS34cUqmEZEQb4xTrsNoJ94nQrmEFOlPKKCx + CsNH3Gj4/RiNLxIBKdwoEVOk/S3yHh2U11ngjQEzVwK0n5CbAGik5UKPQ++k1b2gi3Fbth + M58MJgltc/k9MAAAWQIC946mMvCfH+nFtWQwvczqqpT+O2IhosryvLjmOOZECrBCDv2Dgp + 4kUajUSMes4hFgzqYQZtbjs2v3ul8qhGP0BuPrI2oTPA+8/anF/wDoeyxtRE8dRFMjMHy2 + I6/1pQDuHp626qTd6SVa+LzVfxjVjuLJpIWx2fnTPF/TfrzPOE2it3fwfXzjjFBzRDg0jT + seRZF+Wh/yhFCIdwKYA3C2mJAZR13N1H7xFTNr44hAWLEVZ289ix0ltWY4gi3krOqwYn2g + vNyGWz/k+snqjjR2cg7I1eNCsEzRZn1i0HMYlkggB+g+YwmOG4cnFP6RZU1ZK3/SbA5aMB + QzvSyJZPLIsZxdkdb5Z15AbVN2nhszS0egxGWc7rgi//7ftF9jVL7Oz52ADZY29xowcKF+ + hDAfbXXgVJX9+gTVIqwQkgl260+6uv0szQIABoHkvbaf8c+1WlkmR13EcoHHkfqNSlqXNy + Cx3nZ7BasEipx0Rw2WhNV+B6rZ/CW005GwRfwmdo+hkwELvShBOesyD8JJB11M9qHOhG+h + ieQnhXbsmUE83KI1MTUSq3iEtrhiHa+R2mRqUSgPW8AT306HqzritisVAow/GxgcHSeZ5d + i2ofwNU7YatePfOBEB3F/MsBC9alF+yEZUOSXnyB2omCSwMp50pn2XMKg3B8iZxK54QBdd + don9zNf3smP0HZC+w44mgiMwFTf7CfTbGXo1u3DNCDMcaOvq3dBawvTVzCvMAiELnF7WgL + s7NTDFRn43xXEplIvmUz8rdik4XPaL3srCPPS27H+q6WkFBOrFggK4YzvmliDTpAINK4Xf + k7y4+NabpV1mRKGayrkXcXgG4gkkhEr5zwQHBbXVAyZxOEVgLtA0P+2tL7HW9nM6WN4EF/ + A3bF7wuj8ntVByQqnGC/+8ALolJQ3LKJGbnrJgx9a3AMcMd3G0pkwIDEUPWNoyWhhuaj5H + yQLoaNb8xOD9p4LTGixsoMI1CiJCXWJFVMZ+iM8CKWYqNwXZyiULuvx3Qo6Dz0VaZAzMj+ + bOR9rKfzraOqrg0Wcn9znMDDitAJ61CKi1oks/DZ0+OI+k4YaW2z2IywBuGo/h4xxUe33F + R4WPl0XKGmHKerv5iOaLM+4JDJxVudphWBgU63kG4PGqTFqgbdZL48kqO714GzWVENSCPm + gMwWR6pcZ4Bu1SlDYwkPLPpUi3z8/xawrbszfeDL/di0dxKQVR8LmaErKh9iMZNJEyQLCd + NpUuiqYcdPK968xImWjQi5QPou/R2XTwD/CN3P4chjTQTdVkkgxDhMv78b4GyxawH2H6HZ + 5zItiC6kESXa7dSqhvlm6YLLypeGs1qYJyNLuwzyjrHQFCMIVpCK9H8zJmv9cQ1je7xfnq + rKHin47ujda3F/nbdeX7OfZRF1VxV6XtB/gdPLaaUJeNdxIsCGdl/qU6ENS1yy5vAMqRmi + eszOAqlHkomlSb46OGyIe7iiBYnUAiggUOuHf5+sc9DkBofPo0Ikv0H0gjTIFMmbOfuP4k + IlgKE/KtXuqdZeAH8dUYof0qZVnl+ihIbniJBzxMKhog4yoymJrDea/K6c+j9RDTHfb1Ht + fVvLoq/Rx8kaJaCQ/Uou+c9FSEJnPXvrXhXDCgTQgq6NBpKmvahnzcrwlX3ZLqSmSl3UDx + JoEfkmB24pHL5zlkeuqcbVmS2Wpm1OfFq3fk8Gv0orFph6AnUvtM7e1nPhqqo6g9V1zdqD + GZRUwuyhrj9QJlcUJ5NwXZ+10GNg2rqu3C0zPJbAVb8cjivc+plwDK6vbtLpsL6YtVs2km + Ze4KLFjKvirOtrEUcDcoYnF5M8sddInz2o/sntiWDQookn662OOUXPR4rRbC8tD/EsXOKl + 3LOzbzv5dTxnMe4TjoOct1zbsGU= + -----END OPENSSH PRIVATE KEY----- + publicKey: | + ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDmanENTLBjxp7ZwbNoNaIU9nl7dIPAm0yyVAKPs3d3GSw6VEAxQIEAbPxygxlQta5YZ6oLKVIA7oUadg/r7fWo095Ah9IPDvZOgV1Z0LVM/qGSBLFFMIZqyeA+N28M9LfO2mG2vLrvWRv1jbCKUVOgnWiTisU78ubV26zScTelJh/UQE4bAkdRfs+YfJMvkNm1LpoHIKaaKtSgTrf912L/cIPbW3sM5Vi6j7mR0/Ya2+q+uacpTPL4EMRmF8Fg6/F/OcqIjUcsr5FtI6owXu2GWuIeud28DqHVxXqEZ6ZYR2/J5Y/fOEoTpSJ2fNwvCl1fm2NM8a+Ndngokb40zsn8lDwfslEefRcZfPrDEe97s+kmP5ed5s/xpyVAy3YgAF21HUWqTu4GrS34cUqmEZEQb4xTrsNoJ94nQrmEFOlPKKCxCsNH3Gj4/RiNLxIBKdwoEVOk/S3yHh2U11ngjQEzVwK0n5CbAGik5UKPQ++k1b2gi3FbthM58MJgltc/k9M= user@public-key + privateKeyPassphrase: password diff --git a/docker/sentrius/exampleInstallWithTypes.yml b/docker/sentrius/exampleInstallWithTypes.yml index 39884b34..9118de79 100644 --- a/docker/sentrius/exampleInstallWithTypes.yml +++ b/docker/sentrius/exampleInstallWithTypes.yml @@ -40,6 +40,11 @@ systems: port: 22 host: sentrius-ssh authorizedKeys: ~/.ssh/authorized_keys + - displayName: UNFETTERED-SSH-HOST + sshUser: ubuntu + port: 22 + host: sentrius-bad-ssh + authorizedKeys: ~/.ssh/authorized_keys ## Define groups of users who are assigned to systems ## also entails the configuration that is applied to groupf diff --git a/sentrius-gcp-chart/templates/bad-ssh-deployment.yaml b/sentrius-gcp-chart/templates/bad-ssh-deployment.yaml new file mode 100644 index 00000000..ec3abe87 --- /dev/null +++ b/sentrius-gcp-chart/templates/bad-ssh-deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: sentrius-bad-ssh + labels: + app: sentrius-bad-ssh + release: {{ .Release.Name }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: sentrius-bad-ssh + template: + metadata: + labels: + app: sentrius-bad-ssh + spec: + containers: + - name: sentrius-bad-ssh + image: "{{ .Values.ssh.image.repository }}:{{ .Values.ssh.image.tag }}" + imagePullPolicy: {{ .Values.sentrius.image.pullPolicy }} + ports: + - containerPort: {{ .Values.ssh.port }} + volumeMounts: + - name: config-volume + mountPath: /config + env: + - name: SPRING_DATASOURCE_USERNAME + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-db-secret + key: db-username + - name: SPRING_DATASOURCE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-db-secret + key: db-password + - name: KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Release.Name }}-db-secret + key: keystore-password + volumes: + - name: config-volume + configMap: + name: {{ .Release.Name }}-config diff --git a/sentrius-gcp-chart/templates/bad-ssh-service.yaml b/sentrius-gcp-chart/templates/bad-ssh-service.yaml new file mode 100644 index 00000000..132b0876 --- /dev/null +++ b/sentrius-gcp-chart/templates/bad-ssh-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: sentrius-bad-ssh +spec: + selector: + app: sentrius-bad-ssh # Remove release label from selector + ports: + - protocol: TCP + port: {{ .Values.ssh.port }} + targetPort: {{ .Values.ssh.port }} + type: ClusterIP diff --git a/sentrius-gcp-chart/templates/configmap.yaml b/sentrius-gcp-chart/templates/configmap.yaml index c9520174..3ab8b67b 100644 --- a/sentrius-gcp-chart/templates/configmap.yaml +++ b/sentrius-gcp-chart/templates/configmap.yaml @@ -132,4 +132,4 @@ data: enableInternalAudit=true twopartyapproval.require.explanation.LOCKING_SYSTEMS=false canApproveOwnJITs=false - yamlConfiguration=/app/exampleInstallWithTypes.yml + yamlConfiguration=/app/demoInstaller.yml diff --git a/sentrius-gcp-chart/templates/keycloak-db-deployment.yaml b/sentrius-gcp-chart/templates/keycloak-db-deployment.yaml index 5b96c0ce..eff852a2 100644 --- a/sentrius-gcp-chart/templates/keycloak-db-deployment.yaml +++ b/sentrius-gcp-chart/templates/keycloak-db-deployment.yaml @@ -26,9 +26,12 @@ spec: value: {{ .Values.keycloak.db.password }} - name: POSTGRES_DB value: {{ .Values.keycloak.db.database }} + - name: PGDATA + value: /mnt/keycloak-db/data volumeMounts: - name: keycloak-db-data - mountPath: /var/lib/postgresql/data + mountPath: /mnt/keycloak-db volumes: - name: keycloak-db-data - emptyDir: {} # Replace with persistent volume configuration if needed + persistentVolumeClaim: + claimName: keycloak-db-pvc \ No newline at end of file diff --git a/sentrius-gcp-chart/templates/keycloak-db-pvc.yaml b/sentrius-gcp-chart/templates/keycloak-db-pvc.yaml new file mode 100644 index 00000000..211250de --- /dev/null +++ b/sentrius-gcp-chart/templates/keycloak-db-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: keycloak-db-pvc + labels: + app: keycloak-db +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.keycloak.db.storageSize | default "10Gi" }} diff --git a/sentrius-gcp-chart/templates/postgres-deployment.yaml b/sentrius-gcp-chart/templates/postgres-deployment.yaml index a337e718..b724a723 100644 --- a/sentrius-gcp-chart/templates/postgres-deployment.yaml +++ b/sentrius-gcp-chart/templates/postgres-deployment.yaml @@ -35,3 +35,7 @@ spec: key: db-password - name: POSTGRES_DB value: {{ .Values.postgres.env.POSTGRES_DB }} + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pvc diff --git a/sentrius-gcp-chart/templates/postgres-pvc.yaml b/sentrius-gcp-chart/templates/postgres-pvc.yaml new file mode 100644 index 00000000..b3af1d3f --- /dev/null +++ b/sentrius-gcp-chart/templates/postgres-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + labels: + app: postgres +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.postgres.storageSize | default "10Gi" }} diff --git a/sentrius-gcp-chart/values.yaml b/sentrius-gcp-chart/values.yaml index ffad6ae1..31955b83 100644 --- a/sentrius-gcp-chart/values.yaml +++ b/sentrius-gcp-chart/values.yaml @@ -60,6 +60,7 @@ postgres: repository: postgres tag: 15 port: 5432 + storageSize: 10Gi env: POSTGRES_USER: admin POSTGRES_PASSWORD: password @@ -95,6 +96,7 @@ keycloak: user: keycloak password: password database: keycloak + storageSize: 10Gi replicas: 1 #hostname: sentrius-keycloak