So, for anyone reading this post my concern was for security.

I wanted to keep using the Docker secrets mechanism to give a 
secret.properties to my application instead of using a volume, which is 
less safe. 

First I created a secret in my Docker compose:

secrets:
secrets.properties:
file: ./secrets/secrets.properties

and I exposed this secret for the CAS container

echempad-cas:
(...)
secrets:
- secrets.properties


After that I configured CATALINA_OPTS to point to the location where Docker 
will mount the secret:

echempad-cas:
(...)
- SPRING_PROFILES_ACTIVE=standalone,ldap,db,prod,container,secrets
- 
CATALINA_OPTS=-Dspring.config.additional-location=/run/secrets/secrets.properties

I also added the declaration of SPRING_PROFILES_ACTIVE containing a value 
"secrets", which is the name for the profile that I want active in CAS in 
order to load the variables declared in secrets.properties

That was most of it. I created a small script tool to read all the 
individual secrets and create the secrets.properties file, so I can work 
faster if I need to regenerate my work environment. 

El viernes, 11 de octubre de 2024 a las 17:38:23 UTC+2, Aleix Mariné 
escribió:

> Ray,
>
> I confirm that your approach of giving a destination location to tomcat 
> for a application.properties for the secrets worked (with minor adjusts).
>
> Thank you for your fast response.
>
> I will detail how my solution works in a future post in this 
> conversationfor the record. 
>
> Meanwhile, you can mark this conversation as closed if applicable. 
>
> Thank you so much.
>
>
> Aleix
>
>
>
> El jueves, 10 de octubre de 2024 a las 5:10:07 UTC+2, Ray Bon escribió:
>
>> Aleix,
>>
>> You can use variable substitution for secrets (or anything that might 
>> change between dev and prod). For example, in cas 
>> src/main/resources/application.yml:
>> cas:
>>   authn:
>>     oauth:
>>       access-token:
>>         crypto:
>>           encryption:
>>             key: ${variable-name}
>>
>> Create an application.properties file (the one in docker secrets) with 
>> the variable definitions:
>> variable-name=123abc...
>>
>> You can also put the entire definition in application.properties:
>> cas.service-registry.ldap.bind-credential=password
>>
>> Then provide the destination location to tomcat (or the app server you 
>> are using) on startup:
>>
>> CATALINA_OPTS="-Dspring.config.additional-location=/run/secrets/application.properties
>>  
>> ..."
>>
>> Cas will look in /etc/cas by default (in this case /etc/cas/config), if 
>> you want to put application.properties there (I am not sure what options 
>> you have with docker secrets).
>>
>> Ray
>>
>> P.S. I like the variable approach because all cas config is in one file 
>> and it is stored in our cas-overlay-template git repo. Ops only has to 
>> supply the variable file. You could of course put all config in 
>> application.properites.
>>
>>
>> On Wed, 2024-10-09 at 02:48 -0700, Aleix Mariné wrote:
>>
>> You don't often get email from [email protected]. Learn why this is 
>> important <https://aka.ms/LearnAboutSenderIdentification>
>>
>> Hello, 
>>
>> I am working with CAS 6.6.12 with WAR overlay, standalone and no spring 
>> cloud. I am building a Docker container with my CAS. 
>>
>> Right now I am trying to set some java properties (application 
>> properties) that are secret into the CAS container, but even though they 
>> are set, they do not seem to affect the behavior of CAS as if they were 
>> introduced using a profile config file or other accepted method. I also 
>> could introduce these properties using system variables, command-line args 
>> or profile configuration files, but they are less secure than using Docker 
>> secrets. Due to that, I want to use Docker secrets (which simply put a file 
>> with the secret inside the container, then the app needs to read it and use 
>> it), which forces me to declare the application property programmatically 
>> at startup after reading its value from the corresponding Docker secret. 
>>
>> That is why I am reading all Docker secrets from /run/secrets and setting 
>> them into the corresponding Java property on startup time. To do that I 
>> created a custom class to execute this code on startup. 
>>
>>
>> @Component
>> public class StartupSecretsLoadingimplements 
>> ApplicationListener<ApplicationEnvironmentPreparedEvent> 
>> {
>>
>>
>>
>> private final static String SECRETS_FOLDER = "/run/secrets/";
>>
>> /**
>> * Reads a secret and introduces it into the environment of .application 
>> properties.
>> *
>>
>> * @paramsecretName name of the secret to read from /run/secrets default 
>> Docker secrets folder.
>> * @paramproperty Property to write / update.
>> * @parampropertyOverridesProvides access to a map with all the 
>> properties that will be loaded into Java env.
>> */
>> private voidaddSecretProperty(String secretName, String property, 
>> Map<String, Object> propertyOverrides)
>>
>>
>> {
>> String secretPath = SECRETS_FOLDER + secretName;
>>
>> try {
>> String secretContent = Files.readString(Path.of(secretPath));
>>
>> // Remove trailing newlines (\n or \r\n) but leave spaces intact
>> secretContent = secretContent.replaceAll("[\\r\\n]+$","");
>>
>> // is this line really setting a Java property? 
>> System.setProperty(property, secretContent);
>>
>> // Alternative to set the Java property?
>> propertyOverrides.put(secretName, secretContent);
>>
>> Logger.getGlobal().warning("Read the secret: "+ secretPath + " with 
>> content " + secretContent);
>> } catch (IOException e) {
>> Logger.getGlobal().warning("Failed to read the secret: "+ 
>> e.getMessage());
>> }
>> }
>>
>> /**
>> * Loads all secrets into the Java properties environment
>> */
>>
>> private voidloadSecrets(ConfigurableEnvironment environment) {
>>
>>
>>
>> Map<String, Object> propertyOverrides = new LinkedHashMap<>();
>>
>> try {
>> this.addSecretProperty("CAS_TGC_CRYPTO_ENCRYPTION_KEY",
>> "cas.tgc.crypto.encryption.key", propertyOverrides);
>> this.addSecretProperty("CAS_TGC_CRYPTO_SIGNING_KEY",
>> "cas.tgc.crypto.signing.key", propertyOverrides);
>> this.addSecretProperty("CAS_WEBFLOW_CRYPTO_SIGNING_KEY",
>> "cas.webflow.crypto.signing.key", propertyOverrides);
>> this.addSecretProperty("CAS_WEBFLOW_CRYPTO_ENCRYPTION_KEY",
>> "cas.webflow.crypto.encryption.key", propertyOverrides);
>> this.addSecretProperty("LDAP_TOKEN","cas.authn.ldap[0].bindCredential", 
>> propertyOverrides);
>> this.addSecretProperty("DB_PASSWORD","cas.authn.jdbc.query[0].password", 
>> propertyOverrides);
>> this.addSecretProperty("ORCID_TOKEN","cas.authn.pac4j.oauth2[0].secret", 
>> propertyOverrides);
>> } catch (Exception e) {
>> throw new RuntimeException("Failed to load secrets from "+ 
>> StartupSecretsLoading.SECRETS_FOLDER, e);
>> }
>>
>> // Add the loaded properties to the environment
>>
>> environment.getPropertySources().addFirst(newMapPropertySource(
>> "customSecrets", propertyOverrides));
>>
>>
>> }
>>
>> /**
>> * This method is executed before loading beans and after the basic 
>> environment initializations. You can inject data
>> * needed for the initialization of those beans.
>> * <p>
>>
>> *@param event Event object that contains information about the 
>> ApplicationEnvironmentPreparedEvent event.
>> */
>> @Override
>> public voidonApplicationEvent(ApplicationEnvironmentPreparedEvent event) 
>> {
>>
>>
>> Logger.getGlobal().warning("Loading secrets from "+ SECRETS_FOLDER);
>> this.loadSecrets(event.getEnvironment());
>> Logger.getGlobal().warning("Finished loading secrets from "+ 
>> SECRETS_FOLDER);
>> }
>>
>>
>>
>> So in this method I was trying two different methods of injecting 
>> variables. The first to appear is System.setProperty(property, 
>> secretContent);, which seems to leave the variable empty anyway. 
>>
>> The second that I tried is propertyOverrides.put(secretName, 
>> secretContent);, continued by environment.getPropertySources().addFirst(new 
>> MapPropertySource("customSecrets", propertyOverrides));, which also does 
>> not work. 
>>
>> I read 
>> https://apereo.github.io/cas/7.0.x/configuration/Configuration-Management-Reload.html
>>  
>> from the official documentation, and it seems that loading application 
>> properties programmatically on startup is not trivial. Can someone with 
>> more knowledge hint me in the right direction or explain me how I must 
>> proceed to read a file on startup and declare using its data an application 
>> property that affect the behaviour of CAS as if the property was loaded 
>> using an application profile file? 
>>
>> Thank you very much, I am lost in here and need some help. 
>>
>>
>> Aleix
>>
>>
>>

-- 
- Website: https://apereo.github.io/cas
- List Guidelines: https://goo.gl/1VRrw7
- Contributions: https://goo.gl/mh7qDG
--- 
You received this message because you are subscribed to the Google Groups "CAS 
Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/a/apereo.org/d/msgid/cas-user/9a95efbe-4f55-4ed5-9283-329ff0eef80bn%40apereo.org.

Reply via email to