我想要以下针对微服务环境的加密设置。一个支持 SSL 的 Eureka 服务器,带有云配置服务和多个微服务,只需要连接到发现服务,读取配置并运行它们的东西。一切都通过 HTTPS 和身份验证。
所以...我做了什么:
Eureka 发现服务器,包括/config/* 上的云配置服务 服务器正在将自己注册到 Eureka 服务器,Eureka 仪表板显示该实例。此服务在启用 SSL 和用户身份验证的端口 9001 上运行。该服务照常工作。
然后我创建了一个新的 SpringBoot 服务,它连接到 Eureka 并将自己注册到它。由于自签名证书,我从这篇文章中写了一个小的 SSLConfguration 类:How to override Spring Cloud Eureka default discovery client default ssl context?将我的个人信任库提供给底层的 EurekaClient。 我们所有的服务都配置了application.yml:
spring:
application:
name: mark2
server:
port: 9998
ssl:
enabled: true
key-store: classpath:keystore.p12
key-store-password: changeit
key-store-type: PKCS12
keyAlias: mark2
http:
client:
ssl:
trust-store: classpath:keystore.p12
trust-store-password: changeit
eureka:
client:
serviceUrl:
defaultZone: https://user:pass@localhost:9001/eureka
客户端完美连接并注册。
现在我希望 SpringBoot 服务在引导过程中注册到 Eureka 服务器并从配置服务器加载配置。所以我将 spring、http 和 eureka 设置移到了 bootstrap.yml
spring:
application:
name: mark2
cloud:
config:
username: user
password: password
discovery:
enabled: true
serviceId: EUREKA-DISCOVERY-SERVICE
failFast: true
name: my
profile: settings
# label: v1
http:
client:
ssl:
trust-store: classpath:keystore.p12
trust-store-password: changeit
eureka:
client:
serviceUrl:
defaultZone: https://user:password@localhost:9001/eureka
instance:
metadataMap:
user: user
password: password
configPath: /config
如果服务启动,它会在引导过程中尝试连接到 Eureka 服务器。这不起作用,因为它不信任来自 Eureka 服务器的自签名证书。我不能使用@Configure 或@Bean,因为我处于 Bootstrap 中(至少我发现没有任何作用)。上述带有 SSLConfiguration 的解决方案也不起作用。
好的,然后我将带有 javax.net.ssl.trustStore 等的 JVM args 设置为我的个人信任库。
bootRun {
jvmArgs = [
"-Djavax.net.ssl.trustStore=/absolute/path/to/keystore.p12",
"-Djavax.net.ssl.trustStorePassword=changeit",
"-Djavax.net.ssl.trustStoreType=PKCS12",
]
}
服务启动,Eureka 客户端开始与 Eureka 服务器通信,找到一个 Cloud Configuration 实例并加载一个属性文件,创建 Spring 上下文。该属性仅包含一些测试属性。
但是然后 - 错误:
Field optionalArgs in org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration required a single bean, but 2 were found:
- getTrustStoredEurekaClient: defined by method 'getTrustStoredEurekaClient' in class path resource [de/mark2/SslConfiguration.class]
- discoveryClientOptionalArgs: defined by method 'discoveryClientOptionalArgs' in org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration
好吧,不好但还好。因为我们给了 JVM args,所以我们不应该再使用 SSLConfiguration。已删除。
现在 - 一切正常,服务应该启动嵌入式 Tomcat。但这不起作用,因为另一个异常表明配置的 keystore 为空(“trustAnchors 参数必须为非空”意味着 keystore 存在但不包含任何别名证书)。我认为,JVM args 在嵌入式 Tomcat 中使用,它尝试将信任库用于某种 ClientCertificates 左右。我不知道。
如果我禁用云配置 - 嵌入式 Tomcat 仍然有同样的错误。如果我随后也删除 JVM args - 服务出现。
所以 - 我不知道,我需要做什么,在引导过程中加密通信而不为 Eureka 客户端定义 JVM args 或给嵌入式 tomcat 一个信任库(他不需要)。
请您参考如下方法:
定义 META-INF/spring.factories 并添加 org.springframework.cloud.bootstrap.BootstrapConfiguration = ... 行
类(class)可能是这样的:
@Configuration
@BootstrapConfiguration
public class SslConfiguration {
@Value("${http.client.ssl.trust-store}")
private URL trustStore;
@Value("${http.client.ssl.trust-store-password}")
private String trustStorePassword;
@Bean
public DiscoveryClient.DiscoveryClientOptionalArgs getTrustStoredEurekaClient(SSLContext sslContext) {
DiscoveryClient.DiscoveryClientOptionalArgs args = new DiscoveryClient.DiscoveryClientOptionalArgs();
args.setSSLContext(sslContext);
return args;
}
@Bean
public SSLContext sslContext() throws Exception {
return new SSLContextBuilder().loadTrustMaterial(trustStore, trustStorePassword.toCharArray()).build();
}
}
并且因为现在定义了两次DiscoveryClientOptionalArgs,所以再添加一个类,在Spring Context up之后加载
@Configuration
public class DiscoveryServiceConfiguration {
@Bean
public static BeanFactoryPostProcessor registerPostProcessor() {
return (ConfigurableListableBeanFactory beanFactory) -> {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
for (String beanDefinitionName : registry.getBeanDefinitionNames()) {
if (beanDefinitionName.equalsIgnoreCase("discoveryClientOptionalArgs")) {
BeanDefinition beanDefinition = registry.containsBeanDefinition(beanDefinitionName) ? registry.getBeanDefinition(beanDefinitionName) : null;
if (beanDefinition != null) {
if (registry.containsBeanDefinition(beanDefinitionName)) {
registry.removeBeanDefinition(beanDefinitionName);
}
}
}
}
};
}
}
然后它也起作用了。您不再需要定义 JVM args。