Introduzione rapida alla configurazione di Spring Cloud

1. Panoramica

Spring Cloud Config è l'approccio client / server di Spring per l'archiviazione e il servizio di configurazioni distribuite su più applicazioni e ambienti.

Questo archivio di configurazione è idealmente aggiornato con il controllo della versione di Git e può essere modificato durante il runtime dell'applicazione. Sebbene si adatti molto bene alle applicazioni Spring utilizzando tutti i formati di file di configurazione supportati insieme a costrutti come Environment , PropertySource o @Value , può essere utilizzato in qualsiasi ambiente che esegue qualsiasi linguaggio di programmazione.

In questo articolo, ci concentreremo su un esempio di come configurare un server di configurazione supportato da Git , utilizzarlo in un semplice server di applicazioni REST e configurare un ambiente sicuro che includa i valori delle proprietà crittografate.

2. Configurazione del progetto e dipendenze

Per essere pronti a scrivere del codice, creiamo prima due nuovi progetti Maven . Il progetto server si basa sul modulo spring-cloud-config-server , nonché sui bundle spring-boot-starter-security e spring-boot-starter-web starter:

 org.springframework.cloud spring-cloud-config-server   org.springframework.boot spring-boot-starter-security   org.springframework.boot spring-boot-starter-web 

Tuttavia, per il progetto client avremo bisogno solo dei moduli spring-cloud-starter-config e spring-boot-starter-web :

 org.springframework.cloud spring-cloud-starter-config   org.springframework.boot spring-boot-starter-web 

3. Un'implementazione del server di configurazione

La parte principale dell'applicazione è una classe di configurazione, più specificamente una @SpringBootApplication , che inserisce tutte le impostazioni richieste tramite l' annotazione di configurazione automatica @EnableConfigServer:

@SpringBootApplication @EnableConfigServer public class ConfigServer { public static void main(String[] arguments) { SpringApplication.run(ConfigServer.class, arguments); } } 

Ora dobbiamo configurare la porta del server su cui il nostro server è in ascolto e un Git -url che fornisce il nostro contenuto di configurazione controllato dalla versione. Quest'ultimo può essere utilizzato con protocolli come http , ssh o un semplice file su un filesystem locale.

Suggerimento: se si prevede di utilizzare più istanze del server di configurazione che puntano allo stesso repository di configurazione, è possibile configurare il server per clonare il repository in una cartella temporanea locale. Ma fai attenzione ai repository privati ​​con autenticazione a due fattori, sono difficili da gestire! In tal caso, è più facile clonarli sul tuo filesystem locale e lavorare con la copia.

Sono disponibili anche alcune variabili segnaposto e modelli di ricerca per la configurazione dell'URL del repository ; ma questo va oltre lo scopo del nostro articolo. Se sei interessato, la documentazione ufficiale è un buon punto di partenza.

Abbiamo anche bisogno di impostare un nome utente e una password per l' autenticazione di base nella nostra application.properties per evitare una password generata automaticamente ad ogni riavvio dell'applicazione:

server.port=8888 spring.cloud.config.server.git.uri=ssh://localhost/config-repo spring.cloud.config.server.git.clone-on-start=true spring.security.user.name=root spring.security.user.password=s3cr3t

4. Un repository Git come archivio di configurazione

Per completare il nostro server, dobbiamo inizializzare un repository Git sotto l'URL configurato, creare alcuni nuovi file di proprietà e renderli popolari con alcuni valori.

Il nome del file di configurazione è composto come una normale applicazione Spring.properties , ma invece della parola "applicazione" viene utilizzato un nome configurato, ad es. Il valore della proprietà "spring.application.name" del client, seguito da un trattino e il profilo attivo. Per esempio:

$> git init $> echo 'user.role=Developer' > config-client-development.properties $> echo 'user.role=User' > config-client-production.properties $> git add . $> git commit -m 'Initial config-client properties'

Risoluzione dei problemi: se si verificano problemi di autenticazione relativi a ssh , ricontrolla ~ / .ssh / known_hosts e ~ / .ssh / authorized_keys sul tuo server ssh!

5. Interrogazione della configurazione

Ora siamo in grado di avviare il nostro server. L' API di configurazione supportata da Git fornita dal nostro server può essere interrogata utilizzando i seguenti percorsi:

/{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties

In cui il segnaposto {label} si riferisce a un ramo Git, {application} al nome dell'applicazione del client e {profile} al profilo dell'applicazione attiva corrente del client.

Quindi possiamo recuperare la configurazione per il nostro client di configurazione pianificato in esecuzione sotto il profilo di sviluppo nel branch master tramite:

$> curl //root:[email protected]:8888/config-client/development/master

6. L'implementazione del client

Quindi, prendiamoci cura del cliente. Questa sarà un'applicazione client molto semplice, costituita da un controller REST con un metodo GET .

La configurazione, per recuperare il nostro server, deve essere inserita in un file di risorse denominato bootstrap.application , perché questo file (come suggerisce il nome) verrà caricato molto presto all'avvio dell'applicazione:

@SpringBootApplication @RestController public class ConfigClient { @Value("${user.role}") private String role; public static void main(String[] args) { SpringApplication.run(ConfigClient.class, args); } @GetMapping( value = "/whoami/{username}", produces = MediaType.TEXT_PLAIN_VALUE) public String whoami(@PathVariable("username") String username) { return String.format("Hello! You're %s and you'll become a(n) %s...\n", username, role); } }

Oltre al nome dell'applicazione, inseriamo anche il profilo attivo e i dettagli di connessione nel nostro bootstrap.properties :

spring.application.name=config-client spring.profiles.active=development spring.cloud.config.uri=//localhost:8888 spring.cloud.config.username=root spring.cloud.config.password=s3cr3t

Per verificare, se la configurazione viene ricevuta correttamente dal nostro server e il valore del ruolo viene iniettato nel nostro metodo controller, lo arricciamo semplicemente dopo aver avviato il client:

$> curl //localhost:8080/whoami/Mr_Pink

Se la risposta è la seguente, il nostro server Spring Cloud Config e il suo client stanno funzionando bene per ora:

Hello! You're Mr_Pink and you'll become a(n) Developer...

7. Crittografia e decrittografia

Requisito : per utilizzare chiavi crittograficamente complesse insieme alle funzionalità di crittografia e decrittografia di Spring, è necessario installare nella JVM i "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files" . Questi possono essere scaricati ad esempio da Oracle. Per installare seguire le istruzioni incluse nel download. Alcune distribuzioni Linux forniscono anche un pacchetto installabile tramite i loro gestori di pacchetti.

Since the config server is supporting encryption and decryption of property values, you can use public repositories as storage for sensitive data like usernames and passwords. Encrypted values are prefixed with the string {cipher} and can be generated by an REST-call to the path ‘/encrypt', if the server is configured to use a symmetric key or a key pair.

An endpoint to decrypt is also available. Both endpoints accept a path containing placeholders for the name of the application and its current profile: ‘/*/{name}/{profile}'. This is especially useful for controlling cryptography per client. However, before they become useful, you have to configure a cryptographic key which we will do in the next section.

Tip: If you use curl to call the en-/decryption API, it's better to use the –data-urlencode option (instead of –data/-d), or set the ‘Content-Type' header explicit to ‘text/plain'. This ensures a correct handling of special characters like ‘+' in the encrypted values.

If a value can't be decrypted automatically while fetching through the client, its key is renamed with the name itself, prefixed by the word ‘invalid'. This should prevent, for example the usage of an encrypted value as password.

Tip: When setting-up a repository containing YAML files, you have to surround your encrypted and prefixed values with single-quotes! With Properties this is not the case.

7.1. CSRF

By default Spring Security enables CSRF protection for all the requests sent to our application.

Therefore, to be able to use the /encrypt and /decrypt endpoints, let's disable the CSRF for them:

@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.csrf() .ignoringAntMatchers("/encrypt/**") .ignoringAntMatchers("/decrypt/**"); super.configure(http); } }

7.2. Key Management

The config server is per default enabled to encrypt property values in a symmetric or asymmetric way.

To use symmetric cryptography, you simply have to set the property ‘encrypt.key' in your application.properties to a secret of your choice. Alternatively you can pass-in the environment variable ENCRYPT_KEY.

For asymmetric cryptography, you can set ‘encrypt.key' to a PEM-encoded string value or configure a keystore to use.

Because we need a highly secured environment for our demo server, we chose the latter option and generating a new keystore, including a RSA key-pair, with the Java keytool first:

$> keytool -genkeypair -alias config-server-key \ -keyalg RSA -keysize 4096 -sigalg SHA512withRSA \ -dname 'CN=Config Server,OU=Spring Cloud,O=Baeldung' \ -keypass my-k34-s3cr3t -keystore config-server.jks \ -storepass my-s70r3-s3cr3t

After that, we're adding the created keystore to our server's bootstrap.properties and re-run it:

encrypt.keyStore.location=classpath:/config-server.jks encrypt.keyStore.password=my-s70r3-s3cr3t encrypt.keyStore.alias=config-server-key encrypt.keyStore.secret=my-k34-s3cr3t

As next step we can query the encryption-endpoint and add the response as value to a configuration in our repository:

$> export PASSWORD=$(curl -X POST --data-urlencode d3v3L \ //root:[email protected]:8888/encrypt) $> echo "user.password={cipher}$PASSWORD" >> config-client-development.properties $> git commit -am 'Added encrypted password' $> curl -X POST //root:[email protected]:8888/refresh

To test, if our setup works correctly, we're modifying the ConfigClient class and restart our client:

@SpringBootApplication @RestController public class ConfigClient { ... @Value("${user.password}") private String password; ... public String whoami(@PathVariable("username") String username) { return String.format("Hello! You're %s and you'll become a(n) %s, " + "but only if your password is '%s'!\n", username, role, password); } }

A final query against our client will show us, if our configuration value is being correct decrypted:

$> curl //localhost:8080/whoami/Mr_Pink Hello! You're Mr_Pink and you'll become a(n) Developer, \ but only if your password is 'd3v3L'!

7.3. Using Multiple Keys

If you want to use multiple keys for encryption and decryption, for example: a dedicated one for each served application, you can add another prefix in the form of {name:value} between the {cipher} prefix and the BASE64-encoded property value.

The config server understands prefixes like {secret:my-crypto-secret} or {key:my-key-alias} nearly out-of-the-box. The latter option needs a configured keystore in your application.properties. This keystore is searched for a matching key alias. For example:

user.password={cipher}{secret:my-499-s3cr3t}AgAMirj1DkQC0WjRv... user.password={cipher}{key:config-client-key}AgAMirj1DkQC0WjRv...

For scenarios without keystore you have to implement a @Bean of type TextEncryptorLocator which handles the lookup and returns a TextEncryptor-Object for each key.

7.4. Serving Encrypted Properties

If you want to disable server-side cryptography and handle decryption of property-values locally, you can put the following in your server's application.properties:

spring.cloud.config.server.encrypt.enabled=false

Furthermore you can delete all the other ‘encrypt.*' properties to disable the REST endpoints.

8. Conclusion

Now we are able to create a configuration server to provide a set of configuration files from a Git repository to client applications. There are a few other things you can do with such a server.

For example:

  • Serve configuration in YAML or Properties format instead of JSON – also with placeholders resolved. Which can be useful, when using it in non-Spring environments, where the configuration is not directly mapped to a PropertySource.
  • Serve plain text configuration files – in turn optionally with resolved placeholders. This can be useful for example to provide an environment-dependent logging-configuration.
  • Embed the config server into an application, where it configures itself from a Git repository, instead of running as standalone application serving clients. Therefore some bootstrap properties must be set and/or the @EnableConfigServer annotation must be removed, which depends on the use case.
  • Rendi disponibile il server di configurazione in Spring Netflix Eureka Service Discovery e abilita il rilevamento automatico del server nei client di configurazione. Ciò diventa importante se il server non ha una posizione fissa o si sposta nella sua posizione.

E per concludere, troverai il codice sorgente di questo articolo su Github .