在具有 Java 8 的嵌入式 Tomcat 8.5 上启用 TLS 握手的可观察性(日志记录/指标)

Posted

技术标签:

【中文标题】在具有 Java 8 的嵌入式 Tomcat 8.5 上启用 TLS 握手的可观察性(日志记录/指标)【英文标题】:Enable Observability (Logging/Metrics) of TLS Handshakes on Embedded Tomcat 8.5 with Java 8 【发布时间】:2019-02-16 18:14:23 【问题描述】:

我们正在运行 Spring Boot API,我们在 API 本身中终止 TLS。有几次,我们发现由于有人创建了许多连接(由于客户端证书被拒绝而合法或错误地)或未使用 TLS 恢复,导致大量搜索导致 CPU 使用率过高。

为了防止将来进行这些冗长且代价高昂的搜索,我们希望记录握手失败或成功的时间以及使用会话恢复的原因和是否使用。

我们并没有特别依赖于我们当前的堆栈,升级到不同的服务器(如 Undertow 或 WebFlux)和/或新版本的 Java 也可以。同样,我们可以使用 APR、NIO 或本机绑定来实现这些目标。

以下其他问题表明,目前没有开箱即用的解决方案。他们建议扩展JSSEImplementation 或create customized SSL Socket Factory,或者将NIO 适配器的级别转为Debug。这些解决方案感觉很脆弱,我想知道是否有基于事件或回调的更可扩展的机制。或者,我们可以从 Java 中启用握手日志,但这些日志很冗长,这样做会导致性能显着下降。

Is it possible to do a TLS handshake event in Tomcat? Enable Logging SSL handshake failure(Audit purpose logs) only on Tomcat 8+ with Java

更新1: 我尝试过使用定制的 SSLServerSocketFactory。 sun.security.ssl.SSLServerSocketFactoryImpl 在绑定时返回一个sun.security.ssl.SSLServerSocketImpl,在接受时返回一个不错的SSLSocket。我可以始终包装该接受方法以添加完成处理程序。唯一的缺点是:SSLServerSocketFactoryImpl 是最终的,所以我不能只包装它。这意味着我需要复制大量代码,但它仍然只能为我提供成功握手的指标。复制代码将成为维护负担,因为这是 JRE 特定的代码。

【问题讨论】:

例如事件或回调:已经在 SO 上提出过这个问题,但我记得没有任何好的答案。您还提供了提到的解决方案(扩展 JSSE,实现工厂) @EugèneAdell,确实,以前有人问过。但一个问题是一年前的问题,另一个问题是 2008 年提出的。那个时候会发生很多事情。 关键在于 JSSE 旨在将应用程序与底层 SSL/TLS 处理隔离开来。这是一个好主意,因为它使整个事情可以与不同的提供者进行插拔,但是无法轻松访问回调的这种副作用正在惩罚统计数据收集(协议、客户端使用的密码套件、错误原因)。设计者不想知道为什么客户端不能与服务器协商,他们认为这些客户端是旧的/损坏的并且不有趣。我检查了 OpenSSL 可以做什么,但并没有更好。 有趣的是,HandshakeCompletedEvent.html 确实存在。如果我自己能够参与其中,我至少可以看到成功的连接,并且通过跟踪会话 ID,我可以跟踪恢复。 是的,此事件将在成功次握手时引发。我在客户端将它用于我的client。在服务器端,我认为找出故障原因更有趣。 【参考方案1】:

我的答案可能不是你所期望的,但这是我自己会做的。

首先,我从不在自定义软件上启用 SSL。既不是 Java,也不是 C#,也不是 Python,也不是 javascript。在我的所有解决方案中,它们都通过普通 HTTP 运行。

我委托给 nginx 的所有 TLS 东西。它是可靠的。它很快。它有很多选择。它具有多功能和详细的日志。它有一些基本的访问控制和 DDoS 保护。它封装了部署的细节,并为多个提供的服务提供了一个外观。

开销很小,即使在普通硬件上也能很好地运行。

您需要两个功能:反向代理和详细日志记录。

最简单的配置文件如下所示:

server 
        listen 443 ssl;

        server_name example.com;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;

        location / 
                # Transfer all request to the actual server using HTTP
                proxy_pass http://<server-in-intranet>:12345;
                proxy_set_header Host $host;
        
        # TLS handshake errors are reported at the info level
        error_log /var/log/nginx/example.com/error.log info;
        # Extra ideas about SSL logging: 
        #   https://docs.nginx.com/nginx/admin-guide/monitoring/logging/#tls_sample

        # The certificates from Let's Encrypt are installed by Certbot
        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot

使用此配置,服务器 https://example.com/ 提供您的实际服务器的内容,该服务器在 Intranet 内的某处通过 HTTPS 运行,而实际服务器是纯 HTTP。

使用此设置,我运行用 Go、Javascript 和 Python 编写的服务器,这些服务器在不同的机器上运行,但收集在单一访问点下,例如https://global.name/service1/, https://global.name/service2/, https://global.name/service3/

【讨论】:

这增加了设置的复杂性和另一个跳跃。 Tomcat 提供开箱即用的 OpenSSL 支持,涵盖超过 80% 的所有用例。【参考方案2】:

这是一个单独的服务器还是负载平衡器后面的一组服务器?

您可以考虑“重新部署”服务器,这样您就有一个具有相同配置但启用了 JAVA OPT 调试 ssl:handshake 的副本。

现在,在负载均衡器上,您将部分流量引导到调试服务器,以对您感兴趣的活动进行采样。

或者,您可以在同一台服务器上的另一个打开调试的端口上部署另一个 tomcat 实例。 (这不是一个想法,因为它在您提到的服务器上增加了负载,在请求增加的时候可能已经遇到了麻烦。)

所以也许你没有负载平衡器,但你可能有防火墙,看看你的防火墙是否是有状态的并且可以为你“分割流量”。

如果当前服务器是 linux 服务器,您可以在我上面提到的“双本地安装”示例中使用 iptables 来执行此操作。像这样:https://www.webair.com/community/simple-stateful-load-balancer-with-iptables-and-nat/

无法绕过复杂的解决方案。

如果您没有负载均衡器,您可能需要考虑它,因为它为您提供了很多灵活性来处理各种情况,而不仅仅是这个。

祝你好运

大卫

【讨论】:

以上是关于在具有 Java 8 的嵌入式 Tomcat 8.5 上启用 TLS 握手的可观察性(日志记录/指标)的主要内容,如果未能解决你的问题,请参考以下文章

代码适用于嵌入式Apache Tomcat 8但不适用于9。有什么改变?

嵌入式tomcat 8.0.21中的Spring websocket

在 Spring Boot 上更改嵌入式 tomcat 版本

在 Tomcat 6-Java 7/8 中运行 Tomcat 6-Java 6 WebApps

Springboot嵌入式Tomcat类加载器缓慢

在 Spring Boot 中嵌入 tomcat 中禁用 Jar Scan 的 scanManifest