Introduction rapide à la configuration de Spring Cloud

1. Vue d'ensemble

Spring Cloud Config est l'approche client / serveur de Spring pour stocker et servir des configurations distribuées dans plusieurs applications et environnements.

Ce magasin de configuration est idéalement versionné sous le contrôle de version Git et peut être modifié au moment de l'exécution de l'application. Bien qu'il s'intègre très bien dans les applications Spring utilisant tous les formats de fichiers de configuration pris en charge ainsi que des constructions telles que Environment , PropertySource ou @Value , il peut être utilisé dans n'importe quel environnement exécutant n'importe quel langage de programmation.

Dans cet article, nous allons nous concentrer sur un exemple de configuration d'un serveur de configuration basé sur Git , de l'utiliser dans un simple serveur d'applications REST et de configurer un environnement sécurisé comprenant des valeurs de propriété chiffrées.

2. Configuration du projet et dépendances

Pour nous préparer à écrire du code, nous créons d'abord deux nouveaux projets Maven . Le projet de serveur repose sur le module spring-cloud-config-server , ainsi que sur les bundles de démarrage spring-boot-starter-security et spring-boot-starter-web :

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

Cependant, pour le projet client, nous n'aurons besoin que des modules spring-cloud-starter-config et spring-boot-starter-web :

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

3. Une implémentation de serveur de configuration

La partie principale de l'application est une classe de configuration - plus spécifiquement une @SpringBootApplication - qui extrait toute la configuration requise via l' annotation de configuration automatique @EnableConfigServer:

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

Nous devons maintenant configurer le port du serveur sur lequel notre serveur écoute et un Git -url qui fournit notre contenu de configuration contrôlé par version. Ce dernier peut être utilisé avec des protocoles comme http , ssh ou un simple fichier sur un système de fichiers local.

Conseil: Si vous prévoyez d'utiliser plusieurs instances de serveur de configuration pointant vers le même référentiel de configuration, vous pouvez configurer le serveur pour cloner votre dépôt dans un dossier temporaire local. Mais attention aux référentiels privés avec une authentification à deux facteurs, ils sont difficiles à gérer! Dans un tel cas, il est plus facile de les cloner sur votre système de fichiers local et de travailler avec la copie.

Il existe également des variables d'espace réservé et des modèles de recherche pour configurer l' URL du référentiel ; mais cela dépasse le cadre de notre article. Si vous êtes intéressé, la documentation officielle est un bon point de départ.

Nous devons également définir un nom d'utilisateur et un mot de passe pour l' authentification de base dans notre application.properties afin d'éviter un mot de passe généré automatiquement à chaque redémarrage de l'application:

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 référentiel Git comme stockage de configuration

Pour compléter notre serveur, nous devons initialiser un référentiel Git sous l'url configurée, créer de nouveaux fichiers de propriétés et les vulgariser avec certaines valeurs.

Le nom du fichier de configuration est composé comme une application Spring normale.properties , mais au lieu du mot 'application' un nom configuré, par exemple la valeur de la propriété 'spring.application.name' du client est utilisée, suivie dash et le profil actif. Par exemple:

$> 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'

Dépannage: Si vous rencontrez des problèmes d'authentification liés à ssh , vérifiez à nouveau ~ / .ssh / known_hosts et ~ / .ssh / allowed_keys sur votre serveur ssh!

5. Interrogation de la configuration

Nous pouvons maintenant démarrer notre serveur. L' API de configuration basée sur Git fournie par notre serveur peut être interrogée à l'aide des chemins suivants:

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

Dans lequel l' espace réservé {label} fait référence à une branche Git, {application} au nom de l'application du client et le {profile} au profil d'application actif actuel du client.

Nous pouvons donc récupérer la configuration de notre client de configuration planifié s'exécutant sous le profil de développement dans le maître de succursale via:

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

6. La mise en œuvre du client

Ensuite, prenons soin du client. Ce sera une application client très simple, composée d'un contrôleur REST avec une méthode GET .

La configuration, pour récupérer notre serveur, doit être placée dans un fichier de ressources nommé bootstrap.application , car ce fichier (comme son nom l'indique) sera chargé très tôt au démarrage de l'application:

@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); } }

En plus du nom de l'application, nous mettons également le profil actif et les détails de connexion dans notre 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

Pour tester, si la configuration est correctement reçue de notre serveur et que la valeur du rôle est injectée dans notre méthode de contrôleur, nous la bouclons simplement après le démarrage du client:

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

Si la réponse est la suivante, notre serveur Spring Cloud Config et son client fonctionnent correctement pour le moment:

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

7. Chiffrement et décryptage

Condition : pour utiliser des clés cryptographiquement fortes avec les fonctionnalités de cryptage et de décryptage Spring, vous avez besoin des «Fichiers de stratégie de juridiction de force illimitée Java Cryptography Extension (JCE)» installés dans votre JVM. Ceux-ci peuvent être téléchargés par exemple à partir d'Oracle. Pour installer, suivez les instructions incluses dans le téléchargement. Certaines distributions Linux fournissent également un package installable via leurs gestionnaires de packages.

Étant donné que le serveur de configuration prend en charge le chiffrement et le déchiffrement des valeurs de propriété, vous pouvez utiliser des référentiels publics comme stockage pour les données sensibles telles que les noms d'utilisateur et les mots de passe. Les valeurs chiffrées sont précédées de la chaîne {cipher} et peuvent être générées par un appel REST au chemin «/ encrypt» , si le serveur est configuré pour utiliser une clé symétrique ou une paire de clés.

Un point de terminaison à décrypter est également disponible. Les deux points de terminaison acceptent un chemin contenant des espaces réservés pour le nom de l'application et son profil actuel: '/ * / {name} / {profile}' . Ceci est particulièrement utile pour contrôler la cryptographie par client. Cependant, avant qu'ils ne deviennent utiles, vous devez configurer une clé cryptographique ce que nous ferons dans la section suivante.

Astuce: Si vous utilisez curl pour appeler l'API en- / decryption, il est préférable d'utiliser l' option –data-urlencode (au lieu de –data / -d ) ou de définir l'en-tête 'Content-Type' explicite sur 'text / plain » . Cela garantit une gestion correcte des caractères spéciaux comme «+» dans les valeurs chiffrées.

Si une valeur ne peut pas être déchiffrée automatiquement lors de la récupération via le client, sa clé est renommée avec le nom lui-même, préfixé par le mot «invalide». Cela devrait empêcher, par exemple, l'utilisation d'une valeur chiffrée comme mot de passe.

Conseil: lors de la configuration d'un référentiel contenant des fichiers YAML, vous devez entourer vos valeurs cryptées et préfixées de guillemets simples! Ce n'est pas le cas avec Properties.

7.1. CSRF

Par défaut, Spring Security active la protection CSRF pour toutes les demandes envoyées à notre application.

Par conséquent, pour pouvoir utiliser les points de terminaison / encrypt et / decrypt , désactivons le CSRF pour eux:

@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. Gestion des clés

Le serveur de configuration est par défaut activé pour crypter les valeurs de propriété de manière symétrique ou asymétrique.

Pour utiliser la cryptographie symétrique , il vous suffit de définir la propriété 'encrypt.key' dans votre application.properties sur un secret de votre choix . Vous pouvez également transmettre la variable d'environnement 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.
  • Rendez le serveur de configuration disponible à la découverte de service Spring Netflix Eureka et activez la découverte automatique de serveur dans les clients de configuration. Cela devient important si le serveur n'a pas d'emplacement fixe ou s'il se déplace à son emplacement.

Et pour conclure, vous trouverez le code source de cet article sur Github .