近来,好像人人都在讨论“安全性”。保护应用安全历来挑战重重,上云之后更是难上加难。而一种看起来比较靠谱的解决方案是“零信任”。Gartner 将“零信任”定义为:
……一种在所有计算基础架构中消除绝对信任的方法,“零信任”意味着会有意地且持续地计算和调整信任级别,使得“适时、适度地访问企业资源”得以实现。
但“零信任”究竟是如何在云环境中工作的,有哪些技术可以帮助您实现“零信任”?本文将在一个常见用例的背景下介绍零信任:
假设您是一家拥有各种由 Java 提供支持的 API 和服务的保险公司。现在您已迁移到云端,生产级工作负载由 CI/CD 流水线自动构建,并部署在公有云服务提供商的 Kubernetes 集群中。在处理敏感的客户信息时,一个主要要求就是使用 TLS 加密所有的流量。
您已在边缘负载均衡器和 Ingress Controller 上启用了加密,但对 Ingress Controller 和应用本身之间的流量进行加密的最佳方法是什么?答案就包括了需要启用应用服务器来处理 TLS。
许多 Java 商店使用 Apache Tomcat 作为首选的应用服务器,并将 Spring Boot 作为框架来构建独立的生产就绪型 Spring 应用(比使用 Java 本身更容易)。针对可以处理 HTTPS 流量的应用,本文详细展示了如何为其配置内置 Apache Tomcat 的 Spring Boot(以及 NGINX Unit)。
Spring Boot:HTTPS 流量通信
在撰写本文时,Spring Boot 已在 GitHub 上获得了近 6万个赞,它是 Java 框架领域一颗耀眼的巨星:好上手、轻量级且功能强大。Spring Boot 项目可以通过内置的应用服务器(例如 Apache Tomcat)编译成自包含的 .jar 文件。要启动 Java 服务,只需执行 .jar 文件并开始向暴露的端口(默认为 8080)发送流量即可,非常简单!
为了正确处理 TLS 连接(HTTPS 流量),您还需要为 Java 服务执行以下几个步骤。有关这些步骤的更多详细信息,请参阅 Spring 文档。
这些说明适用于自签名证书和密钥,但对于生产环境,我们强烈建议替换来自官方证书颁发机构 (CA) 的证书密钥对。
创建包含证书和密钥的密钥存储器:
# keytool -genkey -alias tomcat -keyalg RSA -keystore certstore
-
将密钥存储器放在 Tomcat 肯定能访问的容器镜像中。
-
将这些属性添加到 application.properties 文件中,并使用适当的密码替换
secret
:server.port = 8443 server.ssl.key-store = classpath:keystore.jks server.ssl.key-store-password = secret
配置完成后,Spring Boot 应用将监听端口 8443 的 HTTPS 连接情况。但如果您还想接受 HTTP 连接怎么办?一旦在 application.properties 文件中配置了 HTTPS,您就不能再配置 HTTP;您必须在 Java 代码中实现 HTTP 处理。如需了解典型示例,请参阅 GitHub 上的 spring-projects 仓库。
事实证明,将 4 层 TLS 加密委托给应用框架(例如 Spring Boot)是可行的,但却不方便操作。如果您还使用其他语言和框架(例如 Ruby 和 Rails,或 Python 和 Flask)编写应用,则情况会更加复杂,因为每个框架都拥有自己的配置监听器以及处理密钥和证书的方式。但幸运的是,有一些工具可以让事情变得简单得多!
NGINX Unit 前来救场!
NGINX Unit 是一个开源的多语言应用服务器、反向代理和静态文件服务器,由 NGINX 核心工程团队为类 Unix 系统编写。借助 NGINX Unit,您可以使用标准化的 API 同时运行和管理用多种不同语言编写而成的应用,在撰写本文时,它支持除 Java 之外的七种语言:汇编、Go、JavaScript (Node.js®)、Perl、PHP、Python 及 Ruby。
Unit 还允许您单独配置 HTTP 和 HTTPS 接口,而不必再受使用它们的应用的限制。我们来通过 Spring Boot API 示例探索这个强大的功能。首先,我们必须为 Unit 服务器构建 Spring Boot 应用。在 Unit 实现 Java Servlet API 版本 3 时,唯一的变化是在 Gradle 或 Maven 构建定义中增加了一行。我们使用 Gradle 进行测试。
将
war
插件添加到 build.gradle 文件:plugins { id 'org.springframework.boot' version '2.4.4' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' id 'war' }
构建 .war 文件:
# ./gradlew build
- rootProject 在 settings.gradle 文件中进行定义
- 在Version is defined in the build.gradle 文件
-
在名为 config.json 的文件中定义 Unit 配置:
{ "listeners": { "*:8080": { "pass": "applications/java" } }, "applications": { "java": { "user": "unit", "group": "unit", "type": "java", "environment": { "Deployment": "0.0.1" }, "classpath": [], "webapp": "/path/to/build/libs/demo-0.0.1-SNAPSHOT.war" } } }
激活配置(有关详细信息,参阅文档):
# curl -X PUT --data-binary @config.json --unix-socket \ /path/to/control.unit.sock http://localhost/config/applications/java-app
生成的文件是 build/libs/rootProject‑Version.war,其中:
好了,就是这样!Spring Boot 应用现在正在 Unit 上运行,并且不需要 Tomcat 或其他 Java 应用服务器。
启用 HTTPS
您可能会问,“但如何启用 HTTPS 呢?”问得好,开干吧!您可以通过以下步骤轻松启用 HTTPS。(如上文所述,我们使用的是自签名证书。在生产环境中,请您确保使用 CA 签名证书。)
创建自签名证书包:
# cat cert.pem ca.pem key.pem > bundle.pem
-
将证书包上传到 Unit:
# curl -X PUT --data-binary @bundle.pem --unix-socket \ /path/to/control.unit.sock http://localhost/certificates/bundle
在一个名为 listener.json 的文件中定义 HTTPS 监听器的配置:
"127.0.0.1:443": { "pass": "applications/java-app", "tls": { "certificate": "bundle" } }
激活新的监听器:
# curl -X PUT --data-binary @listener.json --unix-socket \ /path/to/control.unit.sock http://localhost/config/listeners
应用现在接受了 TLS 加密连接 — 无需重新启动应用或 Unit。但其最强大之处在于,上述过程同样适用于使用 Unit 支持的任何语言和框架编写的应用。因此,无需深入了解特定语言的详细信息即可配置 HTTPS。
结语
强大的 NGINX Unit 监听器功能使得对于 HTTP 和 HTTPS 的支持变得更简单,并且这种支持完全不受应用的限制,因为加密是应用于监听器层而非应用层的。如要了解服务器名称指示 (SNI) 和自定义 OpenSSL 配置命令等其他 TLS 功能,请参阅 NGINX Unit 文档。
如要开始使用 NGINX Unit,请参阅安装说明。
NGINX Plus 用户可免费获得 NGINX Unit 支持。立即下载 30 天免费试用版,或与我们联系以讨论您的用例。