了解如何在 Quarkus 应用中执行双向 TLS
在本篇文章中,我们将探讨如何在裸金属和 Kubernetes 上手动设置两个 Quarkus 应用之间的双向 TLS 加密。
如何实现?
在两个服务之间实现双向 TLS 的最佳方法是将其委托给基础设施,例如服务网格。这可以实现标准且安全的通信方式,并避免每个应用程序都实现自己的解决方案。但是,您并非总是在尖端环境中工作。
如果您没有 Istio 这样的服务网格环境,让我们看看如何在 Quarkus 中实现 mTLS。

引导
让我们创建我们将要保护的服务器和客户端应用程序。
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-server-mtls \
-DclassName="org.acme.server.mtls.GreetingResource" \
-Dextensions="rest-client, resteasy-jsonb, kubernetes-client" \
-Dpath="/hello-server"
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-client-mtls \
-DclassName="org.acme.client.mtls.GreetingResource" \
-Dextensions="rest-client, resteasy-jsonb, kubernetes-client" \
-Dpath="/hello-client"
证书和信任库生成
当然,您需要服务器、客户端证书和信任库 :)
keytool -genkeypair -storepass password -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore quarkus-server-mtls/src/main/resources/META-INF/resources/server.keystore
keytool -genkeypair -storepass password -keyalg RSA -keysize 2048 -dname "CN=client" -alias client -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore quarkus-client-mtls/src/main/resources/META-INF/resources/client.keystore
在此示例中,我们使用以下方式模拟信任库:
-
client.keystore
作为服务器应用程序的信任库。 -
server.keystore
作为客户端应用程序的信任库。
cp quarkus-server-mtls/src/main/resources/META-INF/resources/server.keystore quarkus-client-mtls/src/main/resources/META-INF/resources/client.truststore
cp quarkus-client-mtls/src/main/resources/META-INF/resources/client.keystore quarkus-server-mtls/src/main/resources/META-INF/resources/server.truststore
Hello Server 应用
让我们打开并配置服务器 quarkus-server-mtls
。
在 Hello Server 应用中启用 SSL
将以下属性添加到您的应用程序 src/main/resources/application.properties
中以启用 SSL。
quarkus.ssl.native=true
quarkus.http.ssl-port=8443
quarkus.http.ssl.certificate.key-store-file=META-INF/resources/server.keystore
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.port=0
quarkus.http.test-port=0
请参阅指南 使用原生 SSL,详细了解 SSL 在 Quarkus 中的工作原理。 |
激活客户端认证
quarkus.http.ssl.client-auth=required
quarkus.http.ssl.certificate.trust-store-file=META-INF/resources/server.truststore
quarkus.http.ssl.certificate.trust-store-password=password
更新 GreetingResource
为了更好地看到响应来自服务器应用程序,让我们更新 org.acme.server.mtls.GreetingResource
类。
@Path("/hello-server")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello from server"; (1)
}
}
1 | 将返回语句更改为“hello from server”。 |
还有测试类。
@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello-server")
.then()
.statusCode(200)
.body(is("hello from server")); (1)
}
}
1 | 将匹配器更改为“hello from server”。 |
Hello Client 应用
此时,服务器应用程序已准备好实现双向 TLS。让我们打开并配置客户端 quarkus-client-mtls
。
为服务器应用添加 REST 客户端
@Path("/")
@ApplicationScoped
@RegisterRestClient
public interface GreetingService {
@GET
@Path("/hello-server")
@Produces(MediaType.TEXT_PLAIN)
String hello();
}
在 org.acme.client.mtls.GreetingResource
中注入 GreetingService REST 客户端。
@Path("/hello-client")
public class GreetingResource {
@Inject (1)
@RestClient (2)
GreetingService greetingService;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return greetingService.hello(); (3)
}
}
1 | CDI @Inject 注解。 |
2 | MicroProfile @RestClient 注解。 |
3 | 将返回语句替换为 greetingService.hello(); 。 |
请参阅指南 rest-client,详细了解。 |
更新单元测试
将 quarkus-junit5-mockito
依赖添加到您的项目中。
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
</dependency>
@QuarkusTest
public class GreetingResourceTest {
@InjectMock (1)
@RestClient (2)
GreetingService greetingService;
@Test
public void testHelloEndpoint() {
Mockito.when(greetingService.hello()).thenReturn("hello from server"); (3)
given()
.when().get("/hello-client")
.then()
.statusCode(200)
.body(is("hello from server"));
}
}
1 | 注入 CDI bean。 |
2 | RestClient 类型。 |
3 | 模拟 hello 请求。 |
请参阅指南 Testing,详细了解。 |
为双向 TLS 配置 MicroProfile REST 客户端
将以下属性添加到您的应用程序 src/main/resources/application.properties
中以启用 SSL。
org.acme.client.mtls.GreetingService/mp-rest/url=https://:8443
org.acme.client.mtls.GreetingService/mp-rest/trustStore=classpath:/META-INF/resources/client.truststore
org.acme.client.mtls.GreetingService/mp-rest/trustStorePassword=password
org.acme.client.mtls.GreetingService/mp-rest/keyStore=classpath:/META-INF/resources/client.keystore
org.acme.client.mtls.GreetingService/mp-rest/keyStorePassword=password
quarkus.ssl.native=true
外部配置
您不希望在应用程序中包含证书,Quarkus 允许您使用外部配置并覆盖运行时应用程序属性。
让我们看看如何在 Kubernetes / OpenShift 中实现。
在应用程序引导过程中,您添加了 kubernetes-config
扩展。该扩展通过直接从 Kubernetes API 读取 ConfigMaps 来工作。
如果您处于一个受限制的环境中,不允许您的服务帐户查看 ConfigMap 的角色,您需要将外部 工作目录对于
volumeMounts 示例
# ... volumes: - name: config configMap: name: client # ... volumeMounts: - name: config mountPath: /deployments |
Secret
创建包含您的证书和信任库的服务器、客户端和信任库 secret。例如:
kubectl create secret generic server --from-file=tls/server/
kubectl create secret generic client --from-file=tls/client/
kubectl create secret generic truststore --from-file=tls/ca/truststore
ConfigMap
创建服务器和客户端 ConfigMap。
kind: ConfigMap
apiVersion: v1
metadata:
name: server
data:
application.properties: |
quarkus.http.ssl.certificate.key-store-file=/deployments/tls/server.keystore
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.ssl.certificate.trust-store-file=/deployments/tls/ca/truststore
quarkus.http.ssl.certificate.trust-store-password=password
kind: ConfigMap
apiVersion: v1
metadata:
name: client
data:
application.properties: |
org.acme.client.mtls.GreetingService/mp-rest/url=https://server:8443
org.acme.client.mtls.GreetingService/mp-rest/trustStore=/deployments/tls/ca/truststore
org.acme.client.mtls.GreetingService/mp-rest/trustStorePassword=password
org.acme.client.mtls.GreetingService/mp-rest/keyStore=/deployments/tls/client.keystore
org.acme.client.mtls.GreetingService/mp-rest/keyStorePassword=password
在服务器和客户端应用程序中启用 kubernetes-config
扩展
为服务器应用程序添加以下属性。
# only when running in prod (Kubernetes environment)
%prod.quarkus.kubernetes-config.enabled=true
quarkus.kubernetes-config.config-maps=server
以及客户端应用程序。
# only when running in prod (Kubernetes environment)
%prod.quarkus.kubernetes-config.enabled=true
quarkus.kubernetes-config.config-maps=client
请参阅指南 Kubernetes Config,详细了解。 |
部署所有内容
以下是客户端应用程序的示例。
kind: Deployment
apiVersion: apps/v1
metadata:
name: client
namespace: mtls
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
volumes:
- name: client
secret:
secretName: client
- name: truststore
secret:
secretName: truststore
containers:
- name: client
image: 'image-registry.openshift-image-registry.svc:5000/mtls/client:latest'
ports:
- containerPort: 8443
protocol: TCP
resources: {}
volumeMounts:
- name: client
readOnly: true
mountPath: /deployments/tls
- name: truststore
readOnly: true
mountPath: /deployments/tls/ca