Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,21 @@ Disable Property: `org.springframework.cloud.bindings.boot.hana.enable`
Type: `config`
Disable Property: `org.springframework.cloud.bindings.boot.config.enable`

| Property | Value |
| -------------------------------------------------- | -------------------- |
| `spring.cloud.config.uri` | `{uri}` |
| `spring.cloud.config.client.oauth2.clientId` | `{client-id}` |
| `spring.cloud.config.client.oauth2.clientSecret` | `{client-secret}` |
| `spring.cloud.config.client.oauth2.accessTokenUri` | `{access-token-uri}` |
| Property | Value |
|------------------------------------------------------|--------------------------------------------------------|
| `spring.cloud.config.uri` | `{uri}` |
| `spring.cloud.config.client.oauth2.clientId` | `{client-id}` |
| `spring.cloud.config.client.oauth2.clientSecret` | `{client-secret}` |
| `spring.cloud.config.client.oauth2.accessTokenUri` | `{access-token-uri}` |
| `spring.cloud.config.tls.enabled` | `true` when `{tls.crt}` and `{tls.key}` are set |
| `spring.cloud.config.tls.key-store` | derived from `{tls.crt}` and `{tls.key}` |
| `spring.cloud.config.tls.key-store-type` | `"PKCS12"` when `{tls.crt}` and `{tls.key}` are set |
| `spring.cloud.config.tls.key-store-password` | random string when `{tls.crt}` and `{tls.key}` are set |
| `spring.cloud.config.tls.key-alias` | `"config"` when `{tls.crt}` and `{tls.key}` are set |
| `spring.cloud.config.tls.key-password` | `""` when `{tls.crt}` and `{tls.key}` are set |
| `spring.cloud.config.tls.trust-store` | derived from `{ca.crt}` when it is set |
| `spring.cloud.config.tls.trust-store-type` | `"PKCS12"` when `{ca.crt}` is set |
| `spring.cloud.config.tls.trust-store-password` | random string when `{ca.crt}` is set |

## SCS Eureka

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@

import org.springframework.cloud.bindings.Binding;
import org.springframework.cloud.bindings.Bindings;
import org.springframework.cloud.bindings.boot.pem.PemSslStoreHelper;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import java.nio.file.Path;
import java.util.Map;


import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled;

/**
Expand All @@ -40,12 +44,42 @@ public void process(Environment environment, Bindings bindings, Map<String, Obje
}

bindings.filterBindings(TYPE).forEach(binding -> {
MapMapper map = new MapMapper(binding.getSecret(), properties);
Map<String, String> secret = binding.getSecret();
MapMapper map = new MapMapper(secret, properties);
map.from("uri").to("spring.cloud.config.uri");
map.from("client-id").to("spring.cloud.config.client.oauth2.clientId");
map.from("client-secret").to("spring.cloud.config.client.oauth2.clientSecret");
map.from("access-token-uri").to("spring.cloud.config.client.oauth2.accessTokenUri");
});

// When tls.crt and tls.key are set, enable mTLS for config client.
String clientKey = secret.get("tls.key");
String clientCert = secret.get("tls.crt");
if (StringUtils.hasText(clientCert) != StringUtils.hasText(clientKey)) {
throw new IllegalArgumentException("binding secret error: tls.key and tls.crt must both be set if either is set");
}

if (clientKey != null && !clientKey.isEmpty()) {
String generatedPassword = PemSslStoreHelper.generatePassword();

// Create a keystore
Path keyFilePath = PemSslStoreHelper.createKeyStoreFile("config-keystore", generatedPassword, clientCert, clientKey, "config");

properties.put("spring.cloud.config.tls.enabled", true);
properties.put("spring.cloud.config.tls.key-alias", "config");
properties.put("spring.cloud.config.tls.key-store", "file:" + keyFilePath);
properties.put("spring.cloud.config.tls.key-store-type", PemSslStoreHelper.PKCS12_STORY_TYPE);
properties.put("spring.cloud.config.tls.key-store-password", generatedPassword);
properties.put("spring.cloud.config.tls.key-password", "");

String caCert = secret.get("ca.crt");
if (caCert != null && !caCert.isEmpty()) {
// Create a truststore from the CA cert
Path trustFilePath = PemSslStoreHelper.createKeyStoreFile("config-truststore", generatedPassword, caCert, null, "ca");
properties.put("spring.cloud.config.tls.trust-store", "file:" + trustFilePath);
properties.put("spring.cloud.config.tls.trust-store-type", PemSslStoreHelper.PKCS12_STORY_TYPE);
properties.put("spring.cloud.config.tls.trust-store-password", generatedPassword);
}
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@
import org.springframework.cloud.bindings.boot.pem.PemSslStoreHelper;
import org.springframework.util.StringUtils;

import java.io.*;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.CertificateException;
import java.nio.file.Path;
import java.util.Map;
import java.util.Random;

import static org.springframework.cloud.bindings.boot.Guards.isTypeEnabled;

Expand Down Expand Up @@ -66,19 +62,14 @@ public void process(Environment environment, Bindings bindings, Map<String, Obje
properties.put("eureka.instance.preferIpAddress", true);
}

Random random = new Random();
String generatedPassword = random.ints(97 /* letter a */, 122 /* letter z */ + 1)
.limit(10)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
String generatedPassword = PemSslStoreHelper.generatePassword();

// Create a trust store from the CA cert
String trustFilePath = Paths.get(System.getProperty("java.io.tmpdir"), "client-truststore.p12").toString();
KeyStore trustStore = PemSslStoreHelper.createKeyStore("trust", "PKCS12", caCert, null, "rootca");
createStoreFile("truststore", generatedPassword, trustFilePath, trustStore);
Path trustFilePath = PemSslStoreHelper.createKeyStoreFile("eureka-truststore", generatedPassword, caCert, null, "rootca");

properties.put("eureka.client.tls.enabled", true);
properties.put("eureka.client.tls.trust-store", "file:"+trustFilePath);
properties.put("eureka.client.tls.trust-store-type", "PKCS12");
properties.put("eureka.client.tls.trust-store-type", PemSslStoreHelper.PKCS12_STORY_TYPE);
properties.put("eureka.client.tls.trust-store-password", generatedPassword);

// When tls.crt and tls.key are set, enable mTLS for Eureka
Expand All @@ -90,41 +81,14 @@ public void process(Environment environment, Bindings bindings, Map<String, Obje
if (clientKey != null && !clientKey.isEmpty()) {

// Create a keystore
String keyFilePath = Paths.get(System.getProperty("java.io.tmpdir"), "client-keystore.p12").toString();
KeyStore keyStore = PemSslStoreHelper.createKeyStore("key", "PKCS12", clientCert, clientKey, "eureka");
createStoreFile("keystore", generatedPassword, keyFilePath, keyStore);
Path keyFilePath = PemSslStoreHelper.createKeyStoreFile("eureka-keystore", generatedPassword, clientCert, clientKey, "eureka");
properties.put("eureka.client.tls.key-alias", "eureka");
properties.put("eureka.client.tls.key-store", "file:" + keyFilePath);
properties.put("eureka.client.tls.key-store-type", "PKCS12");
properties.put("eureka.client.tls.key-store-type", PemSslStoreHelper.PKCS12_STORY_TYPE);
properties.put("eureka.client.tls.key-store-password", generatedPassword);
properties.put("eureka.client.tls.key-password", "");
}
}
});
}

private static void createStoreFile(String storeType, String generatedPassword, String filePath, KeyStore ks) {
try {
FileOutputStream fos = new FileOutputStream(filePath);
try {
ks.store(fos, generatedPassword.toCharArray());
} catch (KeyStoreException e) {
throw new IllegalStateException("Unable to write " + storeType, e);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Cryptographic algorithm not available", e);
} catch (CertificateException e) {
throw new IllegalStateException("Unable to process certificate", e);
} catch (IOException e) {
throw new IllegalStateException("Unable to create " + storeType, e);
} finally {
try {
fos.close();
} catch (IOException e) {
throw new IllegalStateException("Unable to close " + storeType + " output file", e);
}
}
} catch (FileNotFoundException e) {
throw new IllegalStateException("Unable to open " + storeType + " output file", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,76 @@

package org.springframework.cloud.bindings.boot.pem;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Random;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
* helper for creating stores from PEM-encoded certificates and private keys.
*/
public class PemSslStoreHelper {
public static final String PKCS12_STORY_TYPE = "PKCS12";
private static final String DEFAULT_KEY_ALIAS = "ssl";

/**
* Utility method to create a KeyStore
* @param name the name of the keystore
* @param storeType the type of the keystore (JKS, PKCS12, etc.)
* @param certificate a certificate as string that will be added to the keystore
* @param privateKey the keystore private key as string
* Utility method to create a KeyStore and save it in the tmp directory with give name.
* @param name the store file name
* @param password the store password
* @param certificate the certificate to add to the store
* @param privateKey the private key to add to the store
* @param keyAlias the alias
* @return the keystore
* @return the path which store file is saved
*/
public static KeyStore createKeyStore(String name, String storeType, String certificate, String privateKey, String keyAlias) {
public static Path createKeyStoreFile(String name, String password, String certificate, String privateKey, String keyAlias) {
KeyStore store = createKeyStore(certificate, privateKey, keyAlias);

Path path;
try {
path = Files.createTempFile(Paths.get(System.getProperty("java.io.tmpdir")), name, ".p12");
} catch (IOException e) {
throw new IllegalStateException("Unable to create " + name, e);
}

try (FileOutputStream fos = new FileOutputStream(path.toString())) {
store.store(fos, password.toCharArray());
} catch (KeyStoreException e) {
throw new IllegalStateException("Unable to write " + name, e);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Cryptographic algorithm not available", e);
} catch (CertificateException e) {
throw new IllegalStateException("Unable to process certificate", e);
} catch (IOException e) {
throw new IllegalStateException("Unable to create " + name, e);
}
return path;
}

/**
* Generates a password to use for KeyStore and/or TrustStore
* @return the password
*/
public static String generatePassword() {
return new Random().ints(97 /* letter a */, 122 /* letter z */ + 1)
.limit(10)
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}

private static KeyStore createKeyStore(String certificate, String privateKey, String keyAlias) {
try {
Assert.notNull(certificate, "CertificateContent must not be null");
String type = StringUtils.hasText(storeType) ? storeType : KeyStore.getDefaultType();
KeyStore store = KeyStore.getInstance(type);
KeyStore store = KeyStore.getInstance(PKCS12_STORY_TYPE);
store.load(null);
String certificateContent = PemContent.load(certificate);
String privateKeyContent = PemContent.load(privateKey);
Expand All @@ -53,7 +95,7 @@ public static KeyStore createKeyStore(String name, String storeType, String cert
return store;
}
catch (Exception ex) {
throw new IllegalStateException(String.format("Unable to create %s store: %s", name, ex.getMessage()), ex);
throw new IllegalStateException(String.format("Unable to create key/trust store: %s", ex.getMessage()), ex);
}
}

Expand Down
Loading