Skip to content

Commit 07aeb21

Browse files
committed
Merge pull request #28123 from timtebeek
* pr/28123: Polish "Support PEM format for Kafka SSL certs and private key" Support PEM format for Kafka SSL certs and private key Closes gh-28123
2 parents 56b8494 + 0d06a28 commit 07aeb21

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/kafka/KafkaProperties.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import org.springframework.boot.context.properties.ConfigurationProperties;
3737
import org.springframework.boot.context.properties.PropertyMapper;
38+
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
3839
import org.springframework.boot.convert.DurationUnit;
3940
import org.springframework.core.io.Resource;
4041
import org.springframework.kafka.listener.ContainerProperties.AckMode;
@@ -1042,10 +1043,20 @@ public void setMissingTopicsFatal(boolean missingTopicsFatal) {
10421043
public static class Ssl {
10431044

10441045
/**
1045-
* Password of the private key in the key store file.
1046+
* Password of the private key in either key store key or key store file.
10461047
*/
10471048
private String keyPassword;
10481049

1050+
/**
1051+
* Certificate chain in PEM format with a list of X.509 certificates.
1052+
*/
1053+
private String keyStoreCertificateChain;
1054+
1055+
/**
1056+
* Private key in PEM format with PKCS#8 keys.
1057+
*/
1058+
private String keyStoreKey;
1059+
10491060
/**
10501061
* Location of the key store file.
10511062
*/
@@ -1061,6 +1072,11 @@ public static class Ssl {
10611072
*/
10621073
private String keyStoreType;
10631074

1075+
/**
1076+
* Trusted certificates in PEM format with X.509 certificates.
1077+
*/
1078+
private String trustStoreCertificates;
1079+
10641080
/**
10651081
* Location of the trust store file.
10661082
*/
@@ -1089,6 +1105,22 @@ public void setKeyPassword(String keyPassword) {
10891105
this.keyPassword = keyPassword;
10901106
}
10911107

1108+
public String getKeyStoreCertificateChain() {
1109+
return this.keyStoreCertificateChain;
1110+
}
1111+
1112+
public void setKeyStoreCertificateChain(String keyStoreCertificateChain) {
1113+
this.keyStoreCertificateChain = keyStoreCertificateChain;
1114+
}
1115+
1116+
public String getKeyStoreKey() {
1117+
return this.keyStoreKey;
1118+
}
1119+
1120+
public void setKeyStoreKey(String keyStoreKey) {
1121+
this.keyStoreKey = keyStoreKey;
1122+
}
1123+
10921124
public Resource getKeyStoreLocation() {
10931125
return this.keyStoreLocation;
10941126
}
@@ -1113,6 +1145,14 @@ public void setKeyStoreType(String keyStoreType) {
11131145
this.keyStoreType = keyStoreType;
11141146
}
11151147

1148+
public String getTrustStoreCertificates() {
1149+
return this.trustStoreCertificates;
1150+
}
1151+
1152+
public void setTrustStoreCertificates(String trustStoreCertificates) {
1153+
this.trustStoreCertificates = trustStoreCertificates;
1154+
}
1155+
11161156
public Resource getTrustStoreLocation() {
11171157
return this.trustStoreLocation;
11181158
}
@@ -1146,13 +1186,25 @@ public void setProtocol(String protocol) {
11461186
}
11471187

11481188
public Map<String, Object> buildProperties() {
1189+
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleNonNullValuesIn((entries) -> {
1190+
entries.put("spring.kafka.ssl.key-store-key", this.getKeyStoreKey());
1191+
entries.put("spring.kafka.ssl.key-store-location", this.getKeyStoreLocation());
1192+
});
1193+
MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleNonNullValuesIn((entries) -> {
1194+
entries.put("spring.kafka.ssl.trust-store-certificates", this.getTrustStoreCertificates());
1195+
entries.put("spring.kafka.ssl.trust-store-location", this.getTrustStoreLocation());
1196+
});
11491197
Properties properties = new Properties();
11501198
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
11511199
map.from(this::getKeyPassword).to(properties.in(SslConfigs.SSL_KEY_PASSWORD_CONFIG));
1200+
map.from(this::getKeyStoreCertificateChain)
1201+
.to(properties.in(SslConfigs.SSL_KEYSTORE_CERTIFICATE_CHAIN_CONFIG));
1202+
map.from(this::getKeyStoreKey).to(properties.in(SslConfigs.SSL_KEYSTORE_KEY_CONFIG));
11521203
map.from(this::getKeyStoreLocation).as(this::resourceToPath)
11531204
.to(properties.in(SslConfigs.SSL_KEYSTORE_LOCATION_CONFIG));
11541205
map.from(this::getKeyStorePassword).to(properties.in(SslConfigs.SSL_KEYSTORE_PASSWORD_CONFIG));
11551206
map.from(this::getKeyStoreType).to(properties.in(SslConfigs.SSL_KEYSTORE_TYPE_CONFIG));
1207+
map.from(this::getTrustStoreCertificates).to(properties.in(SslConfigs.SSL_TRUSTSTORE_CERTIFICATES_CONFIG));
11561208
map.from(this::getTrustStoreLocation).as(this::resourceToPath)
11571209
.to(properties.in(SslConfigs.SSL_TRUSTSTORE_LOCATION_CONFIG));
11581210
map.from(this::getTrustStorePassword).to(properties.in(SslConfigs.SSL_TRUSTSTORE_PASSWORD_CONFIG));

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/kafka/KafkaPropertiesTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,27 @@
1616

1717
package org.springframework.boot.autoconfigure.kafka;
1818

19+
import java.util.Map;
20+
21+
import org.apache.kafka.common.config.SslConfigs;
1922
import org.junit.jupiter.api.Test;
2023

2124
import org.springframework.boot.autoconfigure.kafka.KafkaProperties.Cleanup;
2225
import org.springframework.boot.autoconfigure.kafka.KafkaProperties.IsolationLevel;
2326
import org.springframework.boot.autoconfigure.kafka.KafkaProperties.Listener;
27+
import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException;
28+
import org.springframework.core.io.ClassPathResource;
2429
import org.springframework.kafka.core.CleanupConfig;
2530
import org.springframework.kafka.listener.ContainerProperties;
2631

2732
import static org.assertj.core.api.Assertions.assertThat;
33+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2834

2935
/**
3036
* Tests for {@link KafkaProperties}.
3137
*
3238
* @author Stephane Nicoll
39+
* @author Madhura Bhave
3340
*/
3441
class KafkaPropertiesTests {
3542

@@ -52,6 +59,37 @@ void listenerDefaultValuesAreConsistent() {
5259
assertThat(listenerProperties.isMissingTopicsFatal()).isEqualTo(container.isMissingTopicsFatal());
5360
}
5461

62+
@Test
63+
void sslPemConfiguration() {
64+
KafkaProperties properties = new KafkaProperties();
65+
properties.getSsl().setKeyStoreKey("-----BEGINkey");
66+
properties.getSsl().setTrustStoreCertificates("-----BEGINtrust");
67+
properties.getSsl().setKeyStoreCertificateChain("-----BEGINchain");
68+
Map<String, Object> consumerProperties = properties.buildConsumerProperties();
69+
assertThat(consumerProperties.get(SslConfigs.SSL_KEYSTORE_KEY_CONFIG)).isEqualTo("-----BEGINkey");
70+
assertThat(consumerProperties.get(SslConfigs.SSL_TRUSTSTORE_CERTIFICATES_CONFIG)).isEqualTo("-----BEGINtrust");
71+
assertThat(consumerProperties.get(SslConfigs.SSL_KEYSTORE_CERTIFICATE_CHAIN_CONFIG))
72+
.isEqualTo("-----BEGINchain");
73+
}
74+
75+
@Test
76+
void sslPropertiesWhenKeyStoreLocationAndKeySetShouldThrowException() {
77+
KafkaProperties properties = new KafkaProperties();
78+
properties.getSsl().setKeyStoreKey("-----BEGIN");
79+
properties.getSsl().setKeyStoreLocation(new ClassPathResource("ksLoc"));
80+
assertThatExceptionOfType(MutuallyExclusiveConfigurationPropertiesException.class)
81+
.isThrownBy(properties::buildConsumerProperties);
82+
}
83+
84+
@Test
85+
void sslPropertiesWhenTrustStoreLocationAndCertificatesSetShouldThrowException() {
86+
KafkaProperties properties = new KafkaProperties();
87+
properties.getSsl().setTrustStoreLocation(new ClassPathResource("tsLoc"));
88+
properties.getSsl().setTrustStoreCertificates("-----BEGIN");
89+
assertThatExceptionOfType(MutuallyExclusiveConfigurationPropertiesException.class)
90+
.isThrownBy(properties::buildConsumerProperties);
91+
}
92+
5593
@Test
5694
void cleanupConfigDefaultValuesAreConsistent() {
5795
CleanupConfig cleanupConfig = new CleanupConfig();

0 commit comments

Comments
 (0)