Spring Boot/Keycloak 通信安全问题

问题描述 投票:0回答:1

我对这个常见问题有一个变体,其他问题没有解决这个问题。我有一个 Spring Boot/Security 等(2.5.6 Boot、5.5.3 Security、Java 17)提供 REST API 和 SPA 代码,使用 Keycloak 23 进行身份验证。两台服务器位于同一台计算机(Wintel)上。我开发了这个,将主机名配置为运行 HTTP 的 localhost,一切正常;用户被重定向到KC进行登录,获取JWT,用于API,提供SPA代码。我更改为 HTTPS 并为 KC 和 SB 制作了一个自签名证书,相同的证书。我配置了它并将其安装在正确的位置。再次一切正常。

我将 KC 和 Spring Boot 配置中的主机名更改为机器的实际名称,当 Spring Security 尝试配置自身以获取 KC 上的 OIDC 端点时,我得到了熟悉的 ResourceAccessException。这是堆栈跟踪的一部分。请注意,没有任何关于无效证书或其他此类指示的信息。异常中指定的 URL 可通过 Curl 访问,并在浏览器中返回预期的 JSON。 Chrome 和 FF 不会抱怨证书不受信任。

Caused by: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://myhost:8443/realms/myrealm/.well-known/openid-configuration": Permission denied: connect; nested exception is java.net.SocketException: Permission denied: connect
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:785) ~[spring-web-5.3.12.jar!/:5.3.12]
        at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:670) ~[spring-web-5.3.12.jar!/:5.3.12]
        at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:155) ~[spring-security-oauth2-client-5.5.3.jar!/:5.5.3]
        at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:208) ~[spring-security-oauth2-client-5.5.3.jar!/:5.5.3]
        ... 97 common frames omitted
Caused by: java.net.SocketException: Permission denied: connect
        at java.base/sun.nio.ch.Net.connect0(Native Method) ~[na:na]
        at java.base/sun.nio.ch.Net.connect(Net.java:579) ~[na:na]

这是 Spring Boot 配置。信任库被注释掉; JVM 应该从 JDK 读取 cacerts。我尝试将 cacerts 放在类路径上,但没有任何变化。 Cacerts 包含密钥库中的证书。

server:
  port: 443
  ssl:
   enabled: true    
   keyStoreType: PKCS12
   key-store: classpath:myhost.p12
   key-store-password: mypass
   key-alias: myhost
   # JVM should read JDK truststore
   # trust store location
   # trust-store: classpath:cacerts
   # trust store password
   # trust-store-password: changeit
spring:
  security:
    oauth2:
      client:
        registration:
          #keycloak: 
          myclient:
            client-id: "myclient"
            authorization-grant-type: "authorization_code"          
            scope: "openid"
            provider: "keycloak"     
            authorization-endpoint: https://myhost:8443/realms/myrealm/protocol/openid-connect/auth
            redirect-uri: 'https://myhost:443'
            issuer-uri: https://myhost:8443/myrealm 
        provider:
          keycloak:
            user-name-attribute: preferred_username
            authorization-endpoint: https://myhost:8443/myrealm/protocol/openid-connect/auth
            authorization-uri: https://myhost:8443/myrealm/
            oidc-provider:  https://myhost:8443/myrealm/.well-known/openid-configuration 
            issuer-uri: https://myhost:8443/myrealm
            token-uri: https://myhost:8443/myrealm/protocol/openid-connect/token 
            user-info-uri: https://myhost:8443/myrealm/protocol/openid-connect/userinfo
            logout-uri: https://myhost:8443/myrealm/protocol/openid-connect/logout    
            
      resourceserver:
        jwt:   
          issuer-uri: https://myhost:8443/myrealm
          jwk-set-uri: https://myhost:8443/auth/myrealm/protocol/openid-connect/certs

这是 Keycloak 配置。密钥库与 Boot 应用程序的类路径上的文件相同

# basic keystore--same file as in Spring Boot app
https-key-store-file=C:/Keycloak/keycloak-23.0.0/conf/certs/myhost.p12
https-key-store-password=mypass

# truststore--JDK truststore, used by JVM
https-trust-store-file=C:/Program Files/Java/jdk-17.0.1/lib/security/cacerts
https-trust-store-password=changeit
https-trust-store-type=jks

hostname=myhost
hostname-strict-https=true

Keycloak 客户端 URI 是尽可能允许的。

根 URL:https://myhost:443 有效的重定向 URI:/* 网络来源:+ *

我尝试记录以获取有关错误的更多信息。 KC 日志记录甚至 TRACE 似乎主要与其数据库(Progres)有关。 KC 23 使用 Quarkus,我打开了 Quarkus HTTP 日志记录,但只收到成功的请求,没有任何错误。

如果重要的话,这种情况会发生在 VPN 上。解析主机名时是否存在问题?我在 myhost 上使用了 nslookup 和 nbtstat,并查看了结果以及我所有的适配器和地址,不知道该怎么想。

感谢您的任何建议。

稍后...

问题似乎是混淆了 IPv4 和 IPv6 地址。非限定名称 myhost 显示为包裹在 v6 或其他内容中的 v4,这会失败。一项建议是在 JVM 中设置这些变量

-Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false

但它不会改变行为。另一种方法是使用 IP 地址,这与证书命名冲突,或者使用完全限定名称(并更改证书)。我认为后一种选择可行,只不过我使用的是 VPN,连接到远程私有域。

VPN 连接为我提供了一个虚拟适配器,并在私有域的 DNS 中放置了一条 A 记录,以及虚拟适配器的 IP。我的计算机的 FQN myhost.privatedomain.com 通过私有域的 DNS 解析为该 IP。该 IP 与私有域的其余部分位于不同的子网中。无论如何,从私有域内,可以访问我的 Keycloak 安装,https://myhost.privatedomain.com:8443

我位于 ISP 路由器 (Verizon) 后面,该路由器具有 DNS、DHCP 和防火墙。它为我提供了一个 DHCP 地址,与虚拟适配器地址不同。 myhost.privatedomain.com 的 NSLOOKUP 通过私有域 DNS 提供虚拟适配器地址。我无法从我的机器访问虚拟适配器地址。

我将 myhost.privatedomain.com 的 A 记录添加到 Verizon 路由器中的 DNS,并更改了虚拟和物理适配器上的首选 DNS。对于私有域 DNS,本地 DNS 显然仍然被绕过,并且为 myhost.privatedomain.com 返回虚拟适配器地址。当 VPN 关闭时,使用本地 DNS,并返回本地地址,即来自 Verizon 路由器的 DHCP 地址。

此目的是为了让 Spring Boot/Security 应用程序联系 Keycloak 服务器;安全配置期间出现故障。这条切线绕着方块很远。

spring-security keycloak
1个回答
0
投票

解决方案是在应用程序配置中使用完全限定名称 myhost.mydomain.com 作为主机名。正如编辑问题中所讨论的,该问题可能是由于单独使用 myhost 时网络堆栈中的 IPv4 和 IPv6 寻址混淆所致。由于在 VPN 上远程工作,解决方案变得很复杂,其中我的计算机位于 VPN 虚拟适配器和 Verizon 路由器/fwall/dns/dhcp 后面。正如所解释的,FQN 无法从我的机器上寻址。我必须在主域中的计算机上“远程工作”。至此,FQN 已正确解析并且应用程序可以正常运行。如果有一个更简单的答案可以允许在 VPN 上工作,我洗耳恭听

© www.soinside.com 2019 - 2024. All rights reserved.