在本文中,我们将学习如何配置Vault PKI引擎并将其与Spring WebFlux集成。使用Vault PKI,您可以轻松生成由CA签名的X.509证书。然后,您的应用程序可以通过REST API获得证书。它的TTL相对较短。每个应用程序实例都是唯一的。此外,我们还可以使用SpringVault模板简化与Vault API的集成。
让我们再多说一点关于安全库的事。它允许您使用UI、CLI或HTTP API保护、存储和控制对令牌、密码、证书和加密密钥的访问。这是一个非常强大的工具。使用Vault,而不是传统的方法,您可以以更动态、云本地的方式管理您的安全性。例如,可以将Vault与数据库后端集成,然后动态生成用户登录名和密码。此外,对于Spring Boot应用程序,您可以利用Spring Cloud Vault项目。
还不止这些。您可以将Vault与来自Hashicorp的其他工具(如Consor或Nomad)集成。换句话说,它允许我们以安全的方式构建云本地平台。
源代码
如果您想自己尝试,您可以随时查看我的源代码。为了做到这一点,您需要克隆我的存储库示例SpringCloudSecurity(https://github.com/piomin/sample-spring-cloud-security.git)。然后,您应该转到网关服务目录,并按照我在下一节中的说明进行操作。示例应用程序充当微服务的API网关。我们使用Spring Cloud Gateway。由于它是建立在Spring WebFlux之上的,所以这个例子非常适合我们当前的文章。
1. 运行Vault
我们将在开发模式下在Docker容器中运行Vault。在该模式下运行的服务器不需要任何进一步的设置,启动后即可使用。启动后,我们的Vault实例在端口8200
上可用。本文中使用的Vault版本为1.7.1。
$ docker run --cap-add=IPC_LOCK -d --name vault -p 8200:8200 vault
可以使用不同的方法登录,但最适合我们的方式是通过令牌。为此,我们必须使用命令docker logs vault
显示容器日志,然后复制根令牌,如下所示。
最后,我们可以登录到Vault web控制台。
2. 启用和配置Vault PKI
有两种方法可以启用和配置Vault PKI:使用CLI或通过UI。大多数文章都描述了配置PKI引擎所需的CLI命令列表。但是,我将使用Vault UI来实现这一点。首先,让我们在主站点上启用一个新引擎。
然后,我们需要选择一种类型的引擎来启用。在我们的例子中,它是PKI证书的选项。
在创建过程中,让我们保留一个默认名称pki。然后,我们需要导航到新启用的引擎并创建一个新角色。角色用于生成证书。我的角色的名称是默认的。这个名称很重要,因为我们必须使用VaultTemplate
从代码中调用它。
我们角色中使用的密钥类型是rsa
。
在创建它之前,我们应该设置一些重要参数。其中之一是TTL,设置为3天。另外,不要忘了检查字段是否允许任何名称,以及是否需要通用名称。它们都与证书中的CN字段相关。因为我们将在CN字段中存储一个用户名,所以我们需要允许它的任何名称。
创建角色后,我们需要配置CA。为此,我们应该首先切换到“配置”选项卡,然后单击“配置”按钮。
之后,让我们选择配置CA。
最后,我们可以创建一个新的CA证书。我们应该将根值保留为CA类型,将内部值保留为类型。默认密钥格式为pem
。我们还可以为CA证书设置一个通用名称。对于角色和CA,值得填写其他字段,例如组织或组织单位的名称。
3. Spring WebFlux与Vault PKI的集成
让我们从依赖性开始。我们需要包括一个用于反应式API的Spring WebFlux启动器和一个用于保护API的Spring Security启动器。spring Vault core可以提供与Vault API的集成。为了能够使用Spring Vault启动应用程序,我还必须包含Jackson库。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-cloud-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后,让我们配置一个Vault模板bean
。它应该使用http
方案和从配置中注入的身份验证令牌。
@Value("vault.token")
private String vaultToken;
@Bean
VaultTemplate vaultTemplate() {
VaultEndpoint e = new VaultEndpoint();
e.setScheme("http");
VaultTemplate template = new VaultTemplate(e, new TokenAuthentication(vaultToken));
return template;
}
Vault模板为与PKI引擎的交互提供专用支持。我们只需要调用传递PKI引擎名称的方法opsForPki
来获得Vault PKI操作实例(1)。然后,我们需要使用Vault CertificateRequest构建一个证书请求。我们可以设置几个参数,但最重要的是CN和证书TTL(2)。最后,我们应该调用issueCertificate方法,传递请求和在Vault PKI上配置的角色的名称(3)。我们的证书已成功生成。现在,我们只需要从响应中获得它。生成的证书、CA证书和私钥在方法返回的CertificateBundle
对象中可用。
private CertificateBundle issueCertificate() throws Exception {
VaultPkiOperations pkiOperations = vaultTemplate.opsForPki("pki"); // (1)
VaultCertificateRequest request = VaultCertificateRequest.builder()
.ttl(Duration.ofHours(12))
.commonName("localhost")
.build(); // (2)
VaultCertificateResponse response = pkiOperations.issueCertificate("default", request); // (3)
CertificateBundle certificateBundle = response.getRequiredData(); // (4)
log.info("Cert-SerialNumber: {}", certificateBundle.getSerialNumber());
return certificateBundle;
}
4. 启用SpringWebFlux安全性
在上一节中,我们已经将SpringWebFlux与Vault PKI集成。最后,我们可以进入实现的最后一步——基于X.509证书启用安全性。为此,我们需要创建一个@Configuration
类。应使用@EnableWebFluxSecurity
(1)对其进行注释。我们还需要通过实现主体提取器从证书中获取用户名。我们将使用SubjectDNX509
(2)和从CN字段读取数据的正确正则表达式。最终配置禁用CSRF、基本身份验证,并使用X509证书启用SSL(3)。我们还需要用一个用户名piotrm
提供UserDetails
接口(4)的实现。
@Configuration
@EnableWebFluxSecurity // (1)
public class SecurityConfig {
@Bean
public SecurityWebFilterChain filterChain(ServerHttpSecurity http) {
SubjectDnX509PrincipalExtractor principalExtractor =
new SubjectDnX509PrincipalExtractor(); // (2)
principalExtractor.setSubjectDnRegex("CN=(.*?)(?:,|$)");
return http.csrf().disable()
.authorizeExchange(exchanges ->
exchanges.anyExchange().authenticated())
.x509()
.principalExtractor(principalExtractor)
.and()
.httpBasic().disable().build(); // (3)
}
@Bean
public MapReactiveUserDetailsService users() { // (4)
UserDetails user1 = User.builder()
.username("piotrm")
.password("{noop}1234")
.roles("USER")
.build();
return new MapReactiveUserDetailsService(user1);
}
}
在最后一步中,我们需要在运行时覆盖Netty服务器SSL配置。我们的定制者应该实现WebServerFactoryCustomizer
接口,并使用NettyReactiveWebServerFactory
。在customize
方法中,我们首先调用负责在Vault中生成证书的方法issueCertificate
(您可以参考上一节了解该方法的实现)(1)。CertificateBundle
包含所有必需的数据。我们可以在其上调用createKeyStore
方法来创建密钥库(2),然后将其保存在文件(3)中。
要覆盖Netty SSL设置,我们应该使用SSL对象。需要启用客户端身份验证(4)。我们还将设置当前创建的密钥库(5)的位置。之后,我们可以继续创建信任库。发卡机构证书可从CertificateBundle
(6)处获得。然后我们应该创建一个新的密钥库,并将CA证书设置为其中的一个条目(7)。最后,我们将把信任库保存到文件中,并在Ssl对象中设置其位置。
@Component
@Slf4j
public class GatewayServerCustomizer implements
WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@SneakyThrows
@Override
public void customize(NettyReactiveWebServerFactory factory) {
String keyAlias = "vault";
CertificateBundle bundle = issueCertificate(); // (1)
KeyStore keyStore = bundle.createKeyStore(keyAlias); // (2)
String keyStorePath = saveKeyStoreToFile("server-key.pkcs12", keyStore); // (3)
Ssl ssl = new Ssl();
ssl.setEnabled(true);
ssl.setClientAuth(Ssl.ClientAuth.NEED); // (4)
ssl.setKeyStore(keyStorePath); // (5)
ssl.setKeyAlias(keyAlias);
ssl.setKeyStoreType(keyStore.getType());
ssl.setKeyPassword("");
ssl.setKeyStorePassword("123456");
X509Certificate caCert = bundle.getX509IssuerCertificate(); // (6)
log.info("CA-SerialNumber: {}", caCert.getSerialNumber());
KeyStore trustStore = KeyStore.getInstance("pkcs12");
trustStore.load(null, null);
trustStore.setCertificateEntry("ca", caCert); // (7)
String trustStorePath = saveKeyStoreToFile("server-trust.pkcs12", trustStore); // (8)
ssl.setTrustStore(trustStorePath); // (9)
ssl.setTrustStorePassword("123456");
ssl.setTrustStoreType(trustStore.getType());
factory.setSsl(ssl);
factory.setPort(8443);
}
}
5. 使用Vault PKI测试Spring WebFlux
让我们运行示例应用程序。它在8443
端口下可用。我们将使用curl工具进行测试。在这样做之前,我们需要生成一个带有私钥的客户端证书。让我们再次转到Vault UI。如果单击默认Vault UI,将重定向到负责生成证书的表单,如下所示。在CommonName
字段中,我们应该提供UserDetails
实现中配置的测试用户名。对我来说,这是piotrm。另外,不要忘记设置正确的TTL。
生成证书后,您将被重定向到带有结果的站点。首先,您应该将字符串与证书一起复制,并将其保存到文件中。对我来说,它是piotrm.crt
。您还可以显示生成的私钥的内容。然后,执行与证书相同的操作。我的文件名是piotrm.key
。
最后,我们可以向示例应用程序发送一个测试请求,传递密钥和证书文件的名称:
$ curl https://localhost:8443/hello -v --key piotrm.key --cert piotrm.crt
原文地址:https://piotrminkowski.com/2021/05/24/ssl-with-spring-webflux-and-vault-pki/
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2327.html
暂无评论