本教程向你展示如何使用 Azure Key Vault 和 Azure 资源的托管标识通过 TLS/SSL 证书保护你的 Spring Boot(包括 Azure Spring 应用程序)应用程序。
生产级 Spring Boot 应用程序,无论是在云端还是在本地,都需要使用标准 TLS 协议对网络流量进行端到端加密。您遇到的大多数 TLS/SSL 证书都可以从公共根证书颁发机构 (CA) 中发现。然而,有时候,这种发现是不可能的。当证书不可发现时,应用程序必须有某种方式来加载此类证书,将它们呈现给入站网络连接,并从出站网络连接中接受它们。
Spring Boot 应用程序通常通过安装证书来启用 TLS。证书安装到运行 Spring Boot 应用程序的 JVM 的本地密钥库中。使用 Azure 上的 Spring,不会在本地安装证书。相反,Microsoft Azure 的 Spring 集成提供了一种安全且无摩擦的方式来借助 Azure Key Vault 和 Azure 资源的托管身份来启用 TLS。
在本教程中,您将学习如何:
- 使用系统分配的托管标识创建 GNU/Linux VM
- 创建 Azure Key Vault
- 创建自签名 TLS/SSL 证书
- 将自签名 TLS/SSL 证书存储在 Azure Key Vault 中
- 运行 Spring Boot 应用程序,其中入站连接的 TLS/SSL 证书来自 Azure Key Vault
- 运行 Spring Boot 应用程序,其中用于出站连接的 TLS/SSL 证书来自 Azure Key Vault
重要的
目前,Spring Cloud Azure Certificate starter 4.x 版本不支持 TLS/mTLS,它们仅自动配置 Key Vault 证书客户端。因此,如果要使用 TLS/mTLS,则无法迁移到版本 4.x。
先决条件
- 如果您没有 Azure 订阅,请在开始之前创建一个免费帐户。
- curl命令。大多数类 UNIX 操作系统都预装了此命令。特定于操作系统的客户端可在官方 curl 网站上获得。
- jq 命令。大多数类 UNIX 操作系统都预装了此命令。官方 jq 网站上提供了特定于操作系统的客户端。
- Azure CLI,版本 2.38 或更高版本
- 受支持的 Java 开发工具包 (JDK),版本 8。有关详细信息,请参阅 Azure 和 Azure Stack 上的 Java 支持。
- Apache Maven,版本 3.0。
重要的
完成本文中的步骤需要 Spring Boot 2.5 或更高版本。
使用系统分配的托管标识创建 GNU/Linux VM
使用以下步骤创建具有系统分配的托管标识的 Azure VM,并准备运行 Spring Boot 应用程序。有关 Azure 资源的托管标识的概述,请参阅什么是 Azure 资源的托管标识?。
- 打开 Bash 外壳。
- 注销并删除一些身份验证文件以删除任何挥之不去的凭据。
az logout
rm ~/.azure/accessTokens.json
rm ~/.azure/azureProfile.json
- 登录到 Azure CLI。
az login
- 设置订阅 ID。请务必将占位符替换为适当的值。
az account set -s <your subscription ID>
- 创建 Azure 资源组。记下资源组名称以备后用。
az group create \
--name <your resource group name> \
--location <your resource group region>
- 设置默认资源组。
az configure --defaults group=<your resource group name>
- 使用 UbuntuServer 提供的映像 UbuntuLTS 创建启用了系统分配的托管标识的 VM 实例。
az vm create \
--name <your VM name> \
--debug \
--generate-ssh-keys \
--assign-identity \
--role contributor \
--scope /subscriptions/<your subscription> \
--image UbuntuLTS \
--admin-username azureuser
在 JSON 输出中,记下 publicIpAddress 和 systemAssignedIdentity 属性的值。您将在本教程的后面部分使用这些值。
笔记
UbuntuLTS 这个名字是统一资源名称 (URN) 的别名,它是为像 UbuntuLTS 这样的流行图像创建的缩写版本。运行以下命令以表格格式显示缓存的流行图像列表:
az vm image list --output table
- 安装微软 OpenJDK。有关 OpenJDK 的完整信息,请参阅 Microsoft Build of OpenJDK。
ssh azureuser@<your VM public IP address>
- 添加存储库。替换以下命令中的版本占位符并执行:
wget https://packages.microsoft.com/config/ubuntu/{version}/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
通过运行以下命令安装 Microsoft Build of OpenJDK:
sudo apt install apt-transport-https
sudo apt update
sudo apt install msopenjdk-11
创建和配置 Azure Key Vault
使用以下步骤创建 Azure Key Vault,并授予 VM 的系统分配托管标识访问 Key Vault 以获取证书的权限。
在资源组中创建 Azure Key Vault。
az keyvault create \
--name <your Key Vault name> \
--location <your resource group region>
export KEY_VAULT_URI=$(az keyvault show --name <your Key Vault name>
| jq -r '.properties.vaultUri')
记下 KEY_VAULT_URI 值。你稍后会用到它。
- 授予 VM 使用证书密钥保管库的权限。
az keyvault set-policy \
--name <your Key Vault name> \
--object-id <your system-assigned identity> \
--secret-permissions get list \
--certificate-permissions get list import
创建并存储自签名 TLS/SSL 证书
本教程中的步骤适用于直接存储在 Azure Key Vault 中的任何 TLS/SSL 证书(包括自签名)。自签名证书不适合在生产中使用,但对开发和测试应用程序很有用。本教程使用自签名证书。要创建证书,请使用以下命令。
az keyvault certificate create \
--vault-name <your Key Vault name> \
--name mycert \
--policy "$(az keyvault certificate get-default-policy)"
使用安全入站连接运行 Spring Boot 应用程序
在本部分中,你将创建一个 Spring Boot 入门应用程序,其中用于入站连接的 TLS/SSL 证书来自 Azure Key Vault。
要创建应用程序,请使用以下步骤:
- 浏览到 https://start.spring.io/。
- 选择此列表后面的图片中显示的选项。
Project: Maven Project
Language: Java
Spring Boot: 2.7.4
Group: com.contoso (You can put any valid Java package name here.)
Artifact: ssltest (You can put any valid Java class name here.)
Packaging: Jar
Java: 11
- 选择添加依赖项....
- 在文本字段中,键入 Spring Web 并按 Ctrl+Enter。
- 在文本字段中键入 Azure Support,然后按 Enter。您的屏幕应如下所示。
- 带有基本选项的 Spring Initializr 的屏幕截图。
- 在页面底部,选择“生成”。
- 出现提示时,将项目下载到本地计算机上的某个路径。本教程使用当前用户主目录中的 ssltest 目录。上面的值将为您提供该目录中的 ssltest.zip 文件。
启用 Spring Boot 应用程序以加载 TLS/SSL 证书
要使应用程序能够加载证书,请使用以下步骤:
- 解压缩 ssltest.zip 文件。
- 删除测试目录及其子目录。本教程忽略测试,因此您可以放心删除该目录。
- 将 src/main/resources 中的 application.properties 重命名为 application.yml。
- 文件布局如下所示。
├── HELP.md
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
├── java
│ └── com
│ └── contoso
│ └── ssltest
│ └── SsltestApplication.java
└── resources
├── application.yml
├── static
└── templates
- 修改 POM 以添加对 azure-spring-boot-starter-keyvault-certificates 的依赖。将以下代码添加到 pom.xml 文件的 <dependencies> 部分。
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>azure-spring-boot-starter-keyvault-certificates</artifactId>
</dependency>
- 编辑 src/main/resources/application.yml 文件,使其具有以下内容。
server:
ssl:
key-alias: <the name of the certificate in Azure Key Vault to use>
key-store-type: AzureKeyVault
trust-store-type: AzureKeyVault
port: 8443
azure:
keyvault:
uri: <the URI of the Azure Key Vault to use>
- 这些值使 Spring Boot 应用程序能够执行 TLS/SSL 证书的加载操作,如本教程开头所述。下表描述了属性值。
Property Name | Explanation |
server.port | The local TCP port on which to listen for HTTPS connections. |
server.ssl.key-alias | The value of the --name argument you passed to az keyvault certificate create . |
server.ssl.key-store-type | Must be AzureKeyVault . |
server.ssl.trust-store-type | Must be AzureKeyVault . |
azure.keyvault.uri | The vaultUri property in the return JSON from az keyvault create . You saved this value in an environment variable. |
唯一特定于 Key Vault 的属性是 azure.keyvault.uri。该应用在其系统分配的托管标识已被授予访问 Key Vault 的 VM 上运行。因此,该应用程序也已被授予访问权限。
这些更改使 Spring Boot 应用程序能够加载 TLS/SSL 证书。在下一节中,您将允许应用程序执行 TLS/SSL 证书的接受操作,如本教程开头所述。
创建一个 Spring Boot REST 控制器
要创建 REST 控制器,请使用以下步骤:
- 编辑 src/main/java/com/contoso/ssltest/SsltestApplication.java 文件,使其具有以下内容。
package com.contoso.ssltest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class SsltestApplication {
public static void main(String[] args) {
SpringApplication.run(SsltestApplication.class, args);
}
@GetMapping(value = "/ssl-test")
public String inbound(){
return "Inbound TLS is working!!";
}
@GetMapping(value = "/exit")
public void exit() {
System.exit(0);
}
}
- 从未经身份验证的 REST GET 调用中调用 System.exit(0) 仅用于演示目的。不要在实际应用程序中使用 System.exit(0)。
此代码说明了本教程开头提到的当前操作。以下列表突出显示了有关此代码的一些详细信息:
- 现在在 Spring Initializr 生成的 SsltestApplication 类上有一个 @RestController 注释。
- 有一个用@GetMapping 注释的方法,其中包含您将进行的HTTP 调用的值。
- 当浏览器向 /ssl-test 路径发出 HTTPS 请求时,入站方法只返回一条问候语。 inbound 方法说明了服务器如何向浏览器提供 TLS/SSL 证书。
- exit 方法将导致 JVM 在被调用时退出。此方法有助于使示例易于在本教程的上下文中运行。
- 打开一个新的 Bash shell 并导航到 ssltest 目录。运行以下命令。
mvn clean package
- Maven 编译代码并将其打包成可执行的 JAR 文件
验证在 <您的资源组名称> 中创建的网络安全组是否允许来自您的 IP 地址的端口 22 和 8443 上的入站流量。要了解如何配置网络安全组规则以允许入站流量,请参阅创建、更改或删除网络安全组的使用安全规则部分。
- 将可执行 JAR 文件放在 VM 上。
cd target
sftp azureuser@<your VM public IP address>
put *.jar
在服务器上运行应用程序
现在您已经构建了 Spring Boot 应用程序并将其上传到 VM,请使用以下步骤在 VM 上运行它并使用 curl 调用 REST 端点。
- 使用 SSH 连接到 VM,然后运行可执行 jar。
set -o noglob
ssh azureuser@<your VM public IP address> "java -jar *.jar"
- 打开一个新的 Bash shell 并执行以下命令以验证服务器是否提供 TLS/SSL 证书。
curl --insecure https://<your VM public IP address>:8443/ssl-test
- 调用退出路径以终止服务器并关闭网络套接字。
curl --insecure https://<your VM public IP address>:8443/exit
现在您已经看到使用自签名 TLS/SSL 证书加载和呈现操作,您将对应用程序进行一些微不足道的更改以查看接受操作。
使用安全出站连接运行 Spring Boot 应用程序
在本节中,你将修改上一节中的代码,以便出站连接的 TLS/SSL 证书来自 Azure Key Vault。因此,从 Azure Key Vault 中可以满足加载、呈现和接受操作。
修改 SsltestApplication 以说明出站 TLS 连接
使用以下步骤修改应用程序:
- 通过将以下代码添加到 pom.xml 文件的 <dependencies> 部分来添加对 Apache HTTP 客户端的依赖。
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
- 添加一个名为 ssl-test-outbound 的新 rest 端点。此端点为自身打开一个 TLS 套接字,并验证 TLS 连接是否接受 TLS/SSL 证书。
用以下代码替换 SsltestApplication.java 的内容。
package com.contoso.ssltest;
import java.security.KeyStore;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.azure.security.keyvault.jca.KeyVaultLoadStoreParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
@SpringBootApplication
@RestController
public class SsltestApplication {
public static void main(String[] args) {
SpringApplication.run(SsltestApplication.class, args);
}
@GetMapping(value = "/ssl-test")
public String inbound(){
return "Inbound TLS is working!!";
}
@GetMapping(value = "/ssl-test-outbound")
public String outbound() throws Exception {
KeyStore azureKeyVaultKeyStore = KeyStore.getInstance("AzureKeyVault");
KeyVaultLoadStoreParameter parameter = new KeyVaultLoadStoreParameter(
System.getProperty("azure.keyvault.uri"));
azureKeyVaultKeyStore.load(parameter);
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(azureKeyVaultKeyStore, null)
.build();
HostnameVerifier allowAll = (String hostName, SSLSession session) -> true;
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, allowAll);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
String sslTest = "https://localhost:8443/ssl-test";
ResponseEntity<String> response
= restTemplate.getForEntity(sslTest, String.class);
return "Outbound TLS " +
(response.getStatusCode() == HttpStatus.OK ? "is" : "is not") + " Working!!";
}
@GetMapping(value = "/exit")
public void exit() {
System.exit(0);
}
}
- 构建应用程序。
cd ssltest
mvn clean package
- 使用本文前面的相同 sftp 命令再次上传应用程序。
cd target
sftp <your VM public IP address>
put *.jar
- 在 VM 上运行应用程序。
set -o noglob
ssh azureuser@<your VM public IP address> "java -jar *.jar"
- 服务器运行后,验证服务器是否接受 TLS/SSL 证书。在发出上一个 curl 命令的同一个 Bash shell 中,运行以下命令。
curl --insecure https://<your VM public IP address>:8443/ssl-test-outbound
您应该会看到消息出站 TLS 正在工作!!。
- 调用退出路径以终止服务器并关闭网络套接字。
curl --insecure https://<your VM public IP address>:8443/exit
你现在已经看到了一个简单的说明,说明了使用存储在 Azure Key Vault 中的自签名 TLS/SSL 证书进行加载、呈现和接受操作。
清理资源
使用完本教程中创建的 Azure 资源后,可以使用以下命令删除它们:
az group delete --name <your resource group name>
Tags
本文:https://architect.pub/tutorial-secure-spring-boot-apps-using-azure-key-vault-certificates