Snabbintroduktion till vårmolnkonfiguration

1. Översikt

Spring Cloud Config är Springs klient- / serverstrategi för att lagra och betjäna distribuerade konfigurationer över flera applikationer och miljöer.

Denna konfigurationsbutik är idealiskt versionerad under Git- versionskontroll och kan modifieras vid programkörning. Även om det passar mycket bra i vårapplikationer som använder alla konfigurationsfilformat som stöds tillsammans med konstruktioner som Environment , PropertySource eller @Value , kan den användas i alla miljöer som kör vilket programmeringsspråk som helst.

I denna skrivning fokuserar vi på ett exempel på hur du konfigurerar en Git- backad konfigurationsserver, använder den i en enkel REST- applikationsserver och ställer in en säker miljö inklusive krypterade egendomsvärden.

2. Projektinställning och beroenden

För att göra dig redo för att skriva lite kod skapar vi två nya Maven- projekt först. Serverprojektet förlitar sig på våren-moln-konfigurationsserver- modulen, liksom på fjäder-start-start-säkerhet och vår-start-start-webb startpaket:

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

Men för klientprojektet behöver vi bara modulerna vår-moln-start-konfiguration och vår-start-start-webb-moduler :

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

3. En konfigurationsserverimplementering

Huvuddelen av programmet är en config klass - närmare bestämt en @SpringBootApplication - som drar i alla nödvändiga inställningen genom auto konfigurera annotation @EnableConfigServer:

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

Nu måste vi konfigurera servern port som vår server lyssnar och en Git -url som ger vår version styrd konfiguration innehåll. Det senare kan användas med protokoll som http , ssh eller en enkel fil på ett lokalt filsystem.

Tips: Om du planerar att använda flera inställningar för konfigurationsserver som pekar på samma konfigurationsförvar kan du konfigurera servern för att klona din repo i en lokal tillfällig mapp. Men var medveten om privata arkiv med tvåfaktorautentisering, de är svåra att hantera! I ett sådant fall är det lättare att klona dem på ditt lokala filsystem och arbeta med kopian.

Det finns också några platshållarvariabler och sökmönster för att konfigurera lagrings-url tillgänglig. men detta ligger utanför ramen för vår artikel. Om du är intresserad är den officiella dokumentationen ett bra ställe att börja.

Vi måste också ställa in ett användarnamn och ett lösenord för Basic-Authentication i vår applikation. Egenskaper för att undvika ett automatiskt genererat lösenord vid varje programstart:

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. Ett Git-arkiv som konfigurationslagring

För att slutföra vår server måste vi initiera ett Git- arkiv under den konfigurerade webbadressen, skapa några nya egenskapsfiler och popularisera dem med vissa värden.

Konfigurationsfilens namn är sammansatt som en vanlig Spring- applikation. Egenskaper , men istället för ordet "applikation" används ett konfigurerat namn, t.ex. värdet på egenskapen "spring.application.name" för klienten, följt av en streck och den aktiva profilen. Till exempel:

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

Felsökning: Om du stöter på ssh- relaterade autentiseringsproblem, dubbelkolla ~ / .ssh / kända_hostar och ~ / .ssh / auktoriserade tangenter på din ssh-server!

5. Fråga konfigurationen

Nu kan vi starta vår server. Det Git- backade konfigurations-API som tillhandahålls av vår server kan frågas med följande sökvägar:

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

I vilken {label} platshållaren hänvisar till en Git-gren, {application} till klientens applikationsnamn och {profile} till klientens aktuella aktiva applikationsprofil.

Så vi kan hämta konfigurationen för vår planerade konfigurationsklient som körs under utvecklingsprofil i filialmästaren via:

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

6. Klientimplementeringen

Låt oss ta hand om klienten. Detta kommer att vara en mycket enkel klientapplikation, bestående av en REST- styrenhet med en GET- metod.

Konfigurationen, för att hämta vår server, måste placeras i en resursfil med namnet bootstrap.application , eftersom den här filen (som namnet antyder) laddas mycket tidigt medan applikationen startar:

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

Förutom applikationsnamnet lägger vi också den aktiva profilen och anslutningsdetaljerna i våra 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

För att testa om konfigurationen är korrekt fått från vår server och rollen värdet blir injiceras i vår controller metod vi krypa helt enkelt efter uppstart kunden:

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

Om svaret är som följer fungerar vår Spring Cloud Config Server och dess klient bra för nu:

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

7. Kryptering och dekryptering

Krav : För att använda kryptografiskt starka nycklar tillsammans med Spring-krypterings- och dekrypteringsfunktioner behöver du 'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files' installerat i din JVM. Dessa kan laddas ner till exempel från Oracle. För att installera följer du instruktionerna i nedladdningen. Vissa Linux-distributioner ger också ett installerbart paket via sina pakethanterare.

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.
  • Gör konfigurationsservern tillgänglig på Spring Netflix Eureka service discovery och aktivera automatisk serveridentifiering i config-klienter. Detta blir viktigt om servern inte har någon fast plats eller om den rör sig på sin plats.

Och för att avsluta, hittar du källkoden till den här artikeln på Github .